mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
401 lines
20 KiB
Swift
401 lines
20 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
|
|
import LegacyInstantVideoController
|
|
import StoryContainerScreen
|
|
import CameraScreen
|
|
import MediaEditorScreen
|
|
import ChatControllerInteraction
|
|
import SavedMessagesScreen
|
|
import WallpaperGalleryScreen
|
|
import ChatMessageNotificationItem
|
|
|
|
public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) {
|
|
if case let .peer(peer) = params.chatLocation {
|
|
let _ = params.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer).startStandalone()
|
|
}
|
|
|
|
var viewForumAsMessages: Signal<Bool, NoError> = .single(false)
|
|
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
|
viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|
|
|> take(1)
|
|
|> map { combinedView in
|
|
guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else {
|
|
return false
|
|
}
|
|
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} else if case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId {
|
|
viewForumAsMessages = params.context.engine.data.get(
|
|
TelegramEngine.EngineData.Item.Peer.DisplaySavedChatsAsTopics()
|
|
)
|
|
|> map { value in
|
|
return !value
|
|
}
|
|
}
|
|
|
|
let _ = (viewForumAsMessages
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { viewForumAsMessages in
|
|
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum), !viewForumAsMessages {
|
|
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
|
|
let chatListCompletion = params.chatListCompletion
|
|
params.navigationController.pushViewController(controller, completion: { [weak controller] in
|
|
guard let controller else {
|
|
return
|
|
}
|
|
if let activateMessageSearch {
|
|
controller.activateSearch(query: activateMessageSearch.1)
|
|
}
|
|
|
|
if let chatListCompletion {
|
|
chatListCompletion(controller)
|
|
}
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
if !viewForumAsMessages, params.subject == nil, case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId {
|
|
if let controller = params.context.sharedContext.makePeerInfoController(context: params.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
|
params.navigationController.pushViewController(controller, animated: params.animated, completion: {
|
|
})
|
|
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, highlight, timecode) = subject {
|
|
if case let .id(messageId) = messageSubject {
|
|
let navigationController = params.navigationController
|
|
let animated = params.animated
|
|
controller.navigateToMessage(messageLocation: .id(messageId, NavigateToMessageParams(timestamp: timecode, quote: (highlight?.quote).flatMap { quote in NavigateToMessageParams.Quote(string: quote.string, offset: quote.offset) })), 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)
|
|
}
|
|
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
|
|
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload)
|
|
}
|
|
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, case let .peer(peer) = params.chatLocation {
|
|
Queue.mainQueue().after(0.1) {
|
|
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, 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)
|
|
|
|
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
|
|
Queue.mainQueue().after(0.1) {
|
|
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload)
|
|
}
|
|
}
|
|
}
|
|
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:
|
|
if params.navigationController.viewControllers.contains(where: { $0 is StoryContainerScreen }) {
|
|
resolvedKeepStack = true
|
|
} else {
|
|
resolvedKeepStack = params.context.sharedContext.immediateExperimentalUISettings.keepChatNavigationStack
|
|
}
|
|
case .always:
|
|
resolvedKeepStack = true
|
|
case .never:
|
|
resolvedKeepStack = false
|
|
}
|
|
if resolvedKeepStack {
|
|
if let pushController = params.pushController {
|
|
pushController(controller, params.animated, {
|
|
params.completion(controller)
|
|
})
|
|
} else {
|
|
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.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 || controller is MediaEditorScreen || controller is CameraScreen {
|
|
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: ChatControllerSubject.MessageHighlight(quote: nil), 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)
|
|
}
|