Swiftgram/submodules/TelegramUI/Sources/NavigateToChatController.swift
2023-02-28 23:25:35 +04:00

326 lines
16 KiB
Swift

import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import Postbox
import AccountContext
import GalleryUI
import InstantPageUI
import ChatListUI
import PeerAvatarGalleryUI
import SettingsUI
import ChatPresentationInterfaceState
import AttachmentUI
import ForumCreateTopicScreen
public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) {
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
for controller in params.navigationController.viewControllers.reversed() {
var chatListController: ChatListControllerImpl?
if let controller = controller as? ChatListControllerImpl {
chatListController = controller
} else if let controller = controller as? TabBarController {
chatListController = controller.currentController as? ChatListControllerImpl
}
if let chatListController = chatListController {
var matches = false
if case let .forum(peerId) = chatListController.location, peer.id == peerId {
matches = true
} else if case let .forum(peerId) = chatListController.effectiveLocation, peer.id == peerId {
matches = true
}
if matches {
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
if let activateMessageSearch = params.activateMessageSearch {
chatListController.activateSearch(query: activateMessageSearch.1)
}
return
}
}
}
let controller = ChatListControllerImpl(context: params.context, location: .forum(peerId: peer.id), controlsHistoryPreload: false, enableDebugActions: false)
let activateMessageSearch = params.activateMessageSearch
params.navigationController.pushViewController(controller, completion: { [weak controller] in
guard let controller, let activateMessageSearch else {
return
}
controller.activateSearch(query: activateMessageSearch.1)
})
return
}
var found = false
var isFirst = true
if params.useExisting {
for controller in params.navigationController.viewControllers.reversed() {
guard let controller = controller as? ChatControllerImpl else {
isFirst = false
continue
}
if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
if let updateTextInputState = params.updateTextInputState {
controller.updateTextInputState(updateTextInputState)
}
var popAndComplete = true
if let subject = params.subject, case let .message(messageSubject, _, timecode) = subject {
if case let .id(messageId) = messageSubject {
let navigationController = params.navigationController
let animated = params.animated
controller.navigateToMessage(messageLocation: .id(messageId, timecode), animated: isFirst, completion: { [weak navigationController, weak controller] in
if let navigationController = navigationController, let controller = controller {
let _ = navigationController.popToViewController(controller, animated: animated)
}
}, customPresentProgress: { [weak navigationController] c, a in
(navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root), with: a)
})
}
popAndComplete = false
} else if params.scrollToEndIfExists && isFirst {
controller.scrollToEndOfHistory()
} else if let search = params.activateMessageSearch {
controller.activateSearch(domain: search.0, query: search.1)
} else if let reportReason = params.reportReason {
controller.beginReportSelection(reason: reportReason)
}
if popAndComplete {
if let _ = params.navigationController.viewControllers.last as? AttachmentController, let controller = params.navigationController.viewControllers[params.navigationController.viewControllers.count - 2] as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation {
} else {
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
}
params.completion(controller)
}
controller.purposefulAction = params.purposefulAction
if let activateInput = params.activateInput {
controller.activateInput(type: activateInput)
}
if params.changeColors {
controller.presentThemeSelection()
}
if let botStart = params.botStart {
controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in
return state.updatedBotStartPayload(botStart.payload)
})
}
if let attachBotStart = params.attachBotStart {
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
}
params.setupController(controller)
found = true
break
}
isFirst = false
}
}
if !found {
let controller: ChatControllerImpl
if let chatController = params.chatController as? ChatControllerImpl {
controller = chatController
if let botStart = params.botStart {
controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in
return state.updatedBotStartPayload(botStart.payload)
})
}
if let attachBotStart = params.attachBotStart {
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
}
if let botAppStart = params.botAppStart {
controller.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload)
}
} else {
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, botAppStart: params.botAppStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack)
}
controller.purposefulAction = params.purposefulAction
if let search = params.activateMessageSearch {
controller.activateSearch(domain: search.0, query: search.1)
}
let resolvedKeepStack: Bool
switch params.keepStack {
case .default:
resolvedKeepStack = params.context.sharedContext.immediateExperimentalUISettings.keepChatNavigationStack
case .always:
resolvedKeepStack = true
case .never:
resolvedKeepStack = false
}
if resolvedKeepStack {
params.navigationController.pushViewController(controller, animated: params.animated, completion: {
params.completion(controller)
})
} else {
let viewControllers = params.navigationController.viewControllers.filter({ controller in
if controller is ForumCreateTopicScreen {
return false
}
if controller is ChatListController {
if let parentGroupId = params.parentGroupId {
return parentGroupId != .root
} else {
return true
}
} else if controller is TabBarController {
return true
} else {
return false
}
})
if viewControllers.isEmpty {
params.navigationController.replaceAllButRootController(controller, animated: params.animated, animationOptions: params.options, completion: {
params.completion(controller)
})
} else {
if params.useBackAnimation {
params.navigationController.viewControllers = [controller] + params.navigationController.viewControllers
params.navigationController.replaceControllers(controllers: viewControllers + [controller], animated: params.animated, options: params.options, completion: {
params.completion(controller)
})
} else {
params.navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: params.animated, options: params.options, completion: {
params.completion(controller)
})
}
}
}
if let activateInput = params.activateInput {
controller.activateInput(type: activateInput)
}
if params.changeColors {
Queue.mainQueue().after(0.1) {
controller.presentThemeSelection()
}
}
}
params.navigationController.currentWindow?.forEachController { controller in
if let controller = controller as? NotificationContainerController {
controller.removeItems { item in
if let item = item as? ChatMessageNotificationItem {
for message in item.messages {
switch params.chatLocation {
case let .peer(peer):
if message.id.peerId == peer.id {
return true
}
case let .replyThread(replyThreadMessage):
if message.id.peerId == replyThreadMessage.messageId.peerId {
return true
}
}
}
}
return false
}
}
}
}
private func findOpaqueLayer(rootLayer: CALayer, layer: CALayer) -> Bool {
if layer.isHidden || layer.opacity < 0.8 {
return false
}
if !layer.isHidden, let backgroundColor = layer.backgroundColor, backgroundColor.alpha > 0.8 {
let coveringRect = layer.convert(layer.bounds, to: rootLayer)
let intersection = coveringRect.intersection(rootLayer.bounds)
let intersectionArea = intersection.width * intersection.height
let rootArea = rootLayer.bounds.width * rootLayer.bounds.height
if !rootArea.isZero && intersectionArea / rootArea > 0.8 {
return true
}
}
if let sublayers = layer.sublayers {
for sublayer in sublayers {
if findOpaqueLayer(rootLayer: rootLayer, layer: sublayer) {
return true
}
}
}
return false
}
public func isInlineControllerForChatNotificationOverlayPresentation(_ controller: ViewController) -> Bool {
if controller is InstantPageController {
return true
}
return false
}
public func isOverlayControllerForChatNotificationOverlayPresentation(_ controller: ContainableController) -> Bool {
if controller is GalleryController || controller is AvatarGalleryController || controller is WallpaperGalleryController || controller is InstantPageGalleryController || controller is InstantVideoController || controller is NavigationController {
return true
}
if controller.isViewLoaded {
if let backgroundColor = controller.view.backgroundColor, !backgroundColor.isEqual(UIColor.clear) {
return true
}
if findOpaqueLayer(rootLayer: controller.view.layer, layer: controller.view.layer) {
return true
}
}
return false
}
public func navigateToForumThreadImpl(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError> {
return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: messageId, preload: false)
|> deliverOnMainQueue
|> beforeNext { [weak context, weak navigationController] result in
guard let context = context, let navigationController = navigationController else {
return
}
var actualActivateInput: ChatControllerActivateInput? = result.isEmpty ? .text : nil
if let activateInput = activateInput {
actualActivateInput = activateInput
}
context.sharedContext.navigateToChatController(
NavigateToChatControllerParams(
navigationController: navigationController,
context: context,
chatLocation: .replyThread(result.message),
chatLocationContextHolder: result.contextHolder,
subject: messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) },
activateInput: actualActivateInput,
keepStack: keepStack
)
)
}
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
}
public func chatControllerForForumThreadImpl(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64) -> Signal<ChatController, NoError> {
return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: nil, preload: false)
|> deliverOnMainQueue
|> `catch` { _ -> Signal<ReplyThreadInfo, NoError> in
return .complete()
}
|> map { result in
return ChatControllerImpl(
context: context,
chatLocation: .replyThread(message: result.message),
chatLocationContextHolder: result.contextHolder
)
}
}
public func navigateToForumChannelImpl(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController) {
let controller = ChatListControllerImpl(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, enableDebugActions: false)
controller.navigationPresentation = .master
navigationController.pushViewController(controller)
}