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 ForumTopicListScreen public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) { var found = false var isFirst = true if params.useExisting { for controller in params.navigationController.viewControllers.reversed() { if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation && (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 { } 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) } } else { controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, 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 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(peerId): if message.id.peerId == peerId { return true } case let .replyThread(replyThreadMessage): if message.id.peerId == replyThreadMessage.messageId.peerId { return true } case .feed: break } } } 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, navigationController: NavigationController) -> Signal { return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: nil, preload: false) |> deliverOnMainQueue |> beforeNext { [weak context, weak navigationController] result in guard let context = context, let navigationController = navigationController else { return } let chatLocation: ChatLocation = .replyThread(message: result.message) let subject: ChatControllerSubject? subject = nil context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty ? .text : nil, keepStack: .always)) } |> ignoreValues |> `catch` { _ -> Signal in return .complete() } } public func navigateToForumChannelImpl(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController) { navigationController.pushViewController(ChatListControllerImpl(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, enableDebugActions: false)) }