diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 555eeda293..d4bd95daf8 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -378,6 +378,16 @@ public enum ChatControllerActivateInput { case entityInput } +public struct ChatNavigationStackItem: Hashable { + public var peerId: EnginePeer.Id + public var threadId: Int64? + + public init(peerId: EnginePeer.Id, threadId: Int64?) { + self.peerId = peerId + self.threadId = threadId + } +} + public final class NavigateToChatControllerParams { public enum Location { case peer(EnginePeer) @@ -434,12 +444,12 @@ public final class NavigateToChatControllerParams { public let options: NavigationAnimationOptions public let parentGroupId: PeerGroupId? public let chatListFilter: Int32? - public let chatNavigationStack: [PeerId] + public let chatNavigationStack: [ChatNavigationStackItem] public let changeColors: Bool public let setupController: (ChatController) -> Void public let completion: (ChatController) -> Void - public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [PeerId] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) { + public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) { self.navigationController = navigationController self.chatController = chatController self.chatLocationContextHolder = chatLocationContextHolder diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 80e10e4ea5..7d10d6811a 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -580,6 +580,14 @@ public enum ChatPresentationInputQueryResult: Equatable { public let ChatControllerCount = Atomic(value: 0) +public final class PeerInfoNavigationSourceTag { + public let peerId: EnginePeer.Id + + public init(peerId: EnginePeer.Id) { + self.peerId = peerId + } +} + public protocol PeerInfoScreen: ViewController { } diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 6028770f0b..3ac553f992 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -86,7 +86,18 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?, public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal { let iconSignal: Signal - if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, clipStyle: round ? .round : .none, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { + + let clipStyle: AvatarNodeClipStyle + if round { + if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + clipStyle = .roundedRect + } else { + clipStyle = .round + } + } else { + clipStyle = .none + } + if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, clipStyle: clipStyle, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { iconSignal = combineLatest(.single(nil) |> then(signal), .single(nil) |> then(fullSizeSignal)) |> mapToSignal { thumbnailImage, fullSizeImage -> Signal in diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index f1dea07f7e..380be7e9bb 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -419,6 +419,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: true, sourceView: sourceNode.view, gesture: gesture) } self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) + + /*self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId) + self.navigationBar?.allowsCustomTransition = { [weak self] in + guard let strongSelf = self else { + return false + } + if strongSelf.navigationBar?.userInfo == nil { + return false + } + return true + }*/ } switch self.location { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 486dab2967..3888ca3751 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1273,7 +1273,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, #if DEBUG //debugSaveState(basePath: basePath + "/db", name: "previous1") - debugRestoreState(basePath: basePath + "/db", name: "previous1") + //debugRestoreState(basePath: basePath + "/db", name: "previous1") #endif let startTime = CFAbsoluteTimeGetCurrent() diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index a08bc82d73..9c5b18ec29 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -1029,7 +1029,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[649453030] = { return Api.messages.MessageEditData.parse_messageEditData($0) } dict[834488621] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) } dict[-1228606141] = { return Api.messages.MessageViews.parse_messageViews($0) } - dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) } + dict[-948520370] = { return Api.messages.Messages.parse_channelMessages($0) } dict[-1938715001] = { return Api.messages.Messages.parse_messages($0) } dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) } dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) } diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index e3b58abe33..c478d9c39a 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1374,16 +1374,16 @@ public extension Api.messages { } public extension Api.messages { enum Messages: TypeConstructorDescription { - case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User]) case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) case messagesNotModified(count: Int32) case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let chats, let users): + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): if boxed { - buffer.appendInt32(1682413576) + buffer.appendInt32(-948520370) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(pts, buffer: buffer, boxed: false) @@ -1395,6 +1395,11 @@ public extension Api.messages { item.serialize(buffer, true) } buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topics.count)) + for item in topics { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) buffer.appendInt32(Int32(chats.count)) for item in chats { item.serialize(buffer, true) @@ -1460,8 +1465,8 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let chats, let users): - return ("channelMessages", [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("count", String(describing: count)), ("offsetIdOffset", String(describing: offsetIdOffset)), ("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))]) + case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users): + return ("channelMessages", [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("count", String(describing: count)), ("offsetIdOffset", String(describing: offsetIdOffset)), ("messages", String(describing: messages)), ("topics", String(describing: topics)), ("chats", String(describing: chats)), ("users", String(describing: users))]) case .messages(let messages, let chats, let users): return ("messages", [("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))]) case .messagesNotModified(let count): @@ -1484,13 +1489,17 @@ public extension Api.messages { if let _ = reader.readInt32() { _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _6: [Api.Chat]? + var _6: [Api.ForumTopic]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self) } - var _7: [Api.User]? + var _7: [Api.Chat]? if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _8: [Api.User]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil @@ -1499,8 +1508,9 @@ public extension Api.messages { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!) + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) } else { return nil diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 4b4fa8e79d..f57b1e5b9d 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -485,7 +485,13 @@ enum LoadMessageHistoryThreadsError { func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, offsetIndex: StoredPeerThreadCombinedState.Index?, limit: Int) -> Signal { let signal: Signal = postbox.transaction { transaction -> Api.InputChannel? in - return transaction.getPeer(peerId).flatMap(apiInputChannel) + guard let channel = transaction.getPeer(peerId) as? TelegramChannel else { + return nil + } + if !channel.flags.contains(.isForum) { + return nil + } + return apiInputChannel(channel) } |> castError(LoadMessageHistoryThreadsError.self) |> mapToSignal { inputChannel -> Signal in diff --git a/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift b/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift index 396b3fc746..ae8c28826a 100644 --- a/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift +++ b/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift @@ -38,7 +38,7 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M apiUsers = users case let .messagesSlice(_, _, _, _, _, _, users): apiUsers = users - case let .channelMessages(_, _, _, _, _, _, users): + case let .channelMessages(_, _, _, _, _, _, _, users): apiUsers = users case .messagesNotModified: apiUsers = [] diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index e8538fcfb9..b83d450fd5 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2086,7 +2086,8 @@ private func resolveAssociatedMessages(postbox: Postbox, network: Network, state return (messages, chats, users) case let .messagesSlice(_, _, _, _, messages, chats, users): return (messages, chats, users) - case let .channelMessages(_, _, _, _, messages, chats, users): + case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users): + let _ = apiTopics return (messages, chats, users) case .messagesNotModified: return ([], [], []) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 35b78402c4..f776f61843 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -93,8 +93,9 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal mapToSignal { threadInfo -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in if let threadInfo = threadInfo { let anchor: HistoryViewInputAnchor - if threadInfo.incomingUnreadCount > 0 && tagMask == nil { + if threadInfo.maxIncomingReadId <= 1 { + anchor = .message(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: 1)) + } else if threadInfo.incomingUnreadCount > 0 && tagMask == nil { let customUnreadMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: threadInfo.maxIncomingReadId) anchor = .message(customUnreadMessageId) } else { diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index 57ff437018..e2cde230dc 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -522,8 +522,9 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac messages = apiMessages chats = apiChats users = apiUsers - case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers channelPts = pts @@ -540,7 +541,8 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac } return validateBatch(postbox: postbox, network: network, transaction: transaction, accountPeerId: accountPeerId, tag: tag, historyState: historyState, signal: signal, previous: previous, messageNamespace: Namespaces.Message.Cloud) - } |> switchToLatest + } + |> switchToLatest } private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: Int32, tag: MessageTags?, messageIds: [MessageId]) -> Signal { @@ -589,8 +591,9 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network messages = apiMessages chats = apiChats users = apiUsers - case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers channelPts = pts @@ -630,8 +633,9 @@ private func validateScheduledMessagesBatch(postbox: Postbox, network: Network, messages = apiMessages chats = apiChats users = apiUsers - case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers case .messagesNotModified: @@ -703,8 +707,9 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran |> map { result -> Set in let apiMessages: [Api.Message] switch result { - case let .channelMessages(_, _, _, _, messages, _, _): + case let .channelMessages(_, _, _, _, messages, apiTopics, _, _): apiMessages = messages + let _ = apiTopics case let .messages(messages, _, _): apiMessages = messages case let .messagesSlice(_, _, _, _, messages, _, _): @@ -951,8 +956,9 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa |> map { result -> Set in let apiMessages: [Api.Message] switch result { - case let .channelMessages(_, _, _, _, messages, _, _): + case let .channelMessages(_, _, _, _, messages, apiTopics, _, _): apiMessages = messages + let _ = apiTopics case let .messages(messages, _, _): apiMessages = messages case let .messagesSlice(_, _, _, _, messages, _, _): diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index fee47cf0b0..dcbab5ae5b 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -143,7 +143,8 @@ private func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMe return (messages, chats, users) case let .messagesSlice(_, _, _, _, messages, chats, users): return (messages, chats, users) - case let .channelMessages(_, _, _, _, messages, chats, users): + case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users): + let _ = apiTopics return (messages, chats, users) case .messagesNotModified: return ([], [], []) @@ -575,8 +576,9 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH messages = apiMessages chats = apiChats users = apiUsers - case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers channelPts = pts @@ -829,8 +831,9 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId messages = apiMessages chats = apiChats users = apiUsers - case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers case .messagesNotModified: diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift index 645e017284..b54dcc1974 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift @@ -128,7 +128,7 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox switch result { case let .messages(messages, _, _): return .single(messages.compactMap({ $0.id() })) - case let .channelMessages(_, _, _, _, messages, _, _): + case let .channelMessages(_, _, _, _, messages, _, _, _): return .single(messages.compactMap({ $0.id() })) case .messagesNotModified: return .single([]) diff --git a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift index 37ffdbb01f..f6d1487941 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift @@ -47,7 +47,7 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId } let apiMessages: [Api.Message] switch result { - case let .channelMessages(_, _, _, _, messages, _, _): + case let .channelMessages(_, _, _, _, messages, _, _, _): apiMessages = messages case let .messages(messages, _, _): apiMessages = messages diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index 4c4b895c0d..85d93b4a32 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -905,11 +905,25 @@ public extension TelegramEngine.EngineData.Item { } } - public struct ThreadData: TelegramEngineDataItem, PostboxViewDataItem { + public struct ThreadData: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public struct Key: Hashable { + public var id: EnginePeer.Id + public var threadId: Int64 + + public init(id: EnginePeer.Id, threadId: Int64) { + self.id = id + self.threadId = threadId + } + } + public typealias Result = MessageHistoryThreadData? fileprivate var id: EnginePeer.Id fileprivate var threadId: Int64 + + public var mapKey: Key { + return Key(id: self.id, threadId: self.threadId) + } public init(id: EnginePeer.Id, threadId: Int64) { self.id = id diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift index f28300ed05..2fd9a13a37 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift @@ -66,7 +66,8 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po return (messages, chats, users) case let .messagesSlice(_, _, _, _, messages, chats, users): return (messages, chats, users) - case let .channelMessages(_, _, _, _, messages, chats, users): + case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users): + let _ = apiTopics return (messages, chats, users) case .messagesNotModified: return ([], [], []) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index c7025fc57e..dfe04fe314 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -74,8 +74,9 @@ private func mergedState(transaction: Transaction, state: SearchMessagesPeerStat let totalCount: Int32 let nextRate: Int32? switch result { - case let .channelMessages(_, _, count, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, count, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers totalCount = count @@ -478,8 +479,9 @@ func _internal_downloadMessage(postbox: Postbox, network: Network, messageId: Me let chats: [Api.Chat] let users: [Api.User] switch result { - case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers case let .messages(apiMessages, apiChats, apiUsers): @@ -562,8 +564,9 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, let chats: [Api.Chat] let users: [Api.User] switch result { - case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers): messages = apiMessages + let _ = apiTopics chats = apiChats users = apiUsers case let .messages(apiMessages, apiChats, apiUsers): @@ -638,7 +641,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre switch result { case let .messages(apiMessages, _, _): messages = apiMessages - case let .channelMessages(_, _, _, _, apiMessages, _, _): + case let .channelMessages(_, _, _, _, apiMessages, _, _, _): messages = apiMessages case let .messagesSlice(_, _, _, _, apiMessages, _, _): messages = apiMessages @@ -668,7 +671,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre switch result { case let .messages(apiMessages, _, _): messages = apiMessages - case let .channelMessages(_, _, _, _, apiMessages, _, _): + case let .channelMessages(_, _, _, _, apiMessages, _, _, _): messages = apiMessages case let .messagesSlice(_, _, _, _, apiMessages, _, _): messages = apiMessages @@ -692,7 +695,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre switch result { case let .messages(apiMessages, _, _): messages = apiMessages - case let .channelMessages(_, _, _, _, apiMessages, _, _): + case let .channelMessages(_, _, _, _, apiMessages, _, _, _): messages = apiMessages case let .messagesSlice(_, _, _, _, apiMessages, _, _): messages = apiMessages diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index 93814a4fc5..5a37f3d620 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -712,7 +712,7 @@ public final class SparseMessageList { } } -public final class SparseMessageScrollingContext { +/*public final class SparseMessageScrollingContext { public struct State: Equatable { public var totalCount: Int public var minTimestamp: Int32 @@ -762,7 +762,7 @@ public final class SparseMessageScrollingContext { case let .messagesSlice(_, count, _, _, apiMessages, _, _): messages = apiMessages totalCount = Int(count) - case let .channelMessages(_, _, count, _, apiMessages, _, _): + case let .channelMessages(_, _, count, _, apiMessages, _, _, _): messages = apiMessages totalCount = Int(count) case .messagesNotModified: @@ -815,7 +815,7 @@ public final class SparseMessageScrollingContext { return Impl(queue: queue, account: account, peerId: peerId) }) } -} +}*/ public final class SparseMessageCalendar { private final class Impl { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index f08a54605f..ee8beb0a8e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -293,9 +293,9 @@ public extension TelegramEngine { return SparseMessageCalendar(account: self.account, peerId: peerId, threadId: threadId, messageTag: tag) } - public func sparseMessageScrollingContext(peerId: EnginePeer.Id) -> SparseMessageScrollingContext { + /*public func sparseMessageScrollingContext(peerId: EnginePeer.Id) -> SparseMessageScrollingContext { return SparseMessageScrollingContext(account: self.account, peerId: peerId) - } + }*/ public func refreshMessageTagStats(peerId: EnginePeer.Id, threadId: Int64?, tags: [EngineMessage.Tags]) -> Signal { let account = self.account @@ -325,7 +325,7 @@ public extension TelegramEngine { switch result { case let .messagesSlice(_, count, _, _, messages, _, _): return (count, messages.first?.id(namespace: Namespaces.Message.Cloud)?.id) - case let .channelMessages(_, _, count, _, messages, _, _): + case let .channelMessages(_, _, count, _, messages, _, _, _): return (count, messages.first?.id(namespace: Namespaces.Message.Cloud)?.id) case let .messages(messages, _, _): return (Int32(messages.count), messages.first?.id(namespace: Namespaces.Message.Cloud)?.id) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift index 4e430287c1..da545effc0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift @@ -79,7 +79,7 @@ func _internal_requestPeerPhotos(postbox: Postbox, network: Network, peerId: Pee let chats: [Api.Chat] let users: [Api.User] switch result { - case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + case let .channelMessages(_, _, _, _, apiMessages, _, apiChats, apiUsers): messages = apiMessages chats = apiChats users = apiUsers diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2624561041..b728c81953 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -219,7 +219,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G public weak var parentController: ViewController? private let currentChatListFilter: Int32? - private let chatNavigationStack: [PeerId] + private let chatNavigationStack: [ChatNavigationStackItem] public var peekActions: ChatControllerPeekActions = .standard private var didSetup3dTouch: Bool = false @@ -494,8 +494,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G override public var customNavigationData: CustomViewControllerNavigationData? { get { - if case let .peer(peerId) = self.chatLocation { - return ChatControllerNavigationData(peerId: peerId) + if let peerId = self.chatLocation.peerId { + return ChatControllerNavigationData(peerId: peerId, threadId: self.chatLocation.threadId) } else { return nil } @@ -534,7 +534,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return title } - public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [PeerId] = []) { + public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) { let _ = ChatControllerCount.modify { value in return value + 1 } @@ -4079,19 +4079,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.controllerInteraction = controllerInteraction - if case let .peer(peerId) = chatLocation, peerId != context.account.peerId { - switch subject { - case .pinnedMessages, .scheduledMessages, .forwardedMessages: - break - default: - self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId) + if chatLocation.threadId == nil { + if let peerId = chatLocation.peerId, peerId != context.account.peerId { + switch subject { + case .pinnedMessages, .scheduledMessages, .forwardedMessages: + break + default: + self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId) + } } - } - self.navigationBar?.allowsCustomTransition = { [weak self] in - guard let strongSelf = self else { - return false + self.navigationBar?.allowsCustomTransition = { [weak self] in + guard let strongSelf = self else { + return false + } + if strongSelf.navigationBar?.userInfo == nil { + return false + } + if strongSelf.navigationBar?.contentNode != nil { + return false + } + return true } - return strongSelf.navigationItem.rightBarButtonItem === strongSelf.chatInfoNavigationButton?.buttonItem } self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) @@ -9870,9 +9878,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var updatedChatNavigationStack = strongSelf.chatNavigationStack - updatedChatNavigationStack.removeAll(where: { $0 == peer.id}) - if case let .peer(peerId) = strongSelf.chatLocation { - updatedChatNavigationStack.insert(peerId, at: 0) + updatedChatNavigationStack.removeAll(where: { $0 == ChatNavigationStackItem(peerId: peer.id, threadId: nil) }) + if let peerId = strongSelf.chatLocation.peerId { + updatedChatNavigationStack.insert(ChatNavigationStackItem(peerId: peerId, threadId: strongSelf.chatLocation.threadId), at: 0) } strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), animated: false, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in @@ -9936,73 +9944,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - var chatNavigationStack: [PeerId] = self.chatNavigationStack - if case let .peer(peerId) = self.chatLocation, let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary { - chatNavigationStack.removeAll() - chatNavigationStack = summary.peerIds.filter({ $0 != peerId }) + var chatNavigationStack: [ChatNavigationStackItem] = self.chatNavigationStack + if let peerId = self.chatLocation.peerId { + if let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary { + chatNavigationStack.removeAll() + chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: peerId, threadId: self.chatLocation.threadId) }) + } + if let _ = self.chatLocation.threadId { + if !chatNavigationStack.contains(ChatNavigationStackItem(peerId: peerId, threadId: nil)) { + chatNavigationStack.append(ChatNavigationStackItem(peerId: peerId, threadId: nil)) + } + } } if !chatNavigationStack.isEmpty { self.chatDisplayNode.navigationBar?.backButtonNode.isGestureEnabled = true self.chatDisplayNode.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in - guard let strongSelf = self else { + guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode, let navigationController = strongSelf.effectiveNavigationController else { gesture.cancel() return } - - let _ = (strongSelf.context.engine.data.get(EngineDataList( - chatNavigationStack.map(TelegramEngine.EngineData.Item.Peer.Peer.init) - )) - |> deliverOnMainQueue).start(next: { peerList in - guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode else { - return - } - - let peers = peerList.compactMap { $0 } - - let avatarSize = CGSize(width: 28.0, height: 28.0) - - var items: [ContextMenuItem] = [] - for peer in peers { - let title: String - let iconSource: ContextMenuActionItemIconSource? - if peer.id == strongSelf.context.account.peerId { - title = strongSelf.presentationData.strings.DialogList_SavedMessages - iconSource = nil - } else { - title = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) - iconSource = ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer, size: avatarSize)) - } - - let isSavedMessages = peer.id == strongSelf.context.account.peerId - - items.append(.action(ContextMenuActionItem(text: title, icon: { _ in - if isSavedMessages { - return generateAvatarImage(size: avatarSize, icon: savedMessagesIcon, iconScale: 0.5, color: .blue) - } - return nil - }, iconSource: iconSource, action: { _, f in - f(.default) - - guard let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController else { - return - } - - let nextFolderId: Int32? = strongSelf.currentChatListFilter - - var updatedChatNavigationStack = strongSelf.chatNavigationStack - if let index = updatedChatNavigationStack.firstIndex(of: peer.id) { - updatedChatNavigationStack.removeSubrange(0 ..< (index + 1)) - } - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), useBackAnimation: true, animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in - let _ = nextController - })) - }))) - } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - strongSelf.presentInGlobalOverlay(contextController) - }) + let nextFolderId: Int32? = strongSelf.currentChatListFilter + PeerInfoScreenImpl.displayChatNavigationMenu( + context: strongSelf.context, + chatNavigationStack: chatNavigationStack, + nextFolderId: nextFolderId, + parentController: strongSelf, + backButtonView: backButtonNode.view, + navigationController: navigationController, + gesture: gesture + ) } } } @@ -10455,6 +10426,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState) } interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) + if case .peer = self.chatLocation, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()) + } let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in return interfaceState }).start() @@ -17633,33 +17607,35 @@ func peerMessageSelectedReactions(context: AccountContext, message: Message) -> final class ChatControllerNavigationData: CustomViewControllerNavigationData { let peerId: PeerId + let threadId: Int64? - init(peerId: PeerId) { + init(peerId: PeerId, threadId: Int64?) { self.peerId = peerId + self.threadId = threadId } func combine(summary: CustomViewControllerNavigationDataSummary?) -> CustomViewControllerNavigationDataSummary? { if let summary = summary as? ChatControllerNavigationDataSummary { - return summary.adding(peerId: self.peerId) + return summary.adding(peerNavigationItem: ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)) } else { - return ChatControllerNavigationDataSummary(peerIds: [self.peerId]) + return ChatControllerNavigationDataSummary(peerNavigationItems: [ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)]) } } } final class ChatControllerNavigationDataSummary: CustomViewControllerNavigationDataSummary { - let peerIds: [PeerId] + let peerNavigationItems: [ChatNavigationStackItem] - init(peerIds: [PeerId]) { - self.peerIds = peerIds + init(peerNavigationItems: [ChatNavigationStackItem]) { + self.peerNavigationItems = peerNavigationItems } - func adding(peerId: PeerId) -> ChatControllerNavigationDataSummary { - var peerIds = self.peerIds - if let index = peerIds.firstIndex(of: peerId) { - peerIds.removeSubrange(0 ... index) + func adding(peerNavigationItem: ChatNavigationStackItem) -> ChatControllerNavigationDataSummary { + var peerNavigationItems = self.peerNavigationItems + if let index = peerNavigationItems.firstIndex(of: peerNavigationItem) { + peerNavigationItems.removeSubrange(0 ... index) } - peerIds.insert(peerId, at: 0) - return ChatControllerNavigationDataSummary(peerIds: peerIds) + peerNavigationItems.insert(peerNavigationItem, at: 0) + return ChatControllerNavigationDataSummary(peerNavigationItems: peerNavigationItems) } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index 2fbc11de14..ed72d5a18f 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -110,6 +110,12 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess canScrollToRead = false } + if case let .replyThread(message) = chatLocation, message.isForumPost { + if case let .message(index) = view.anchorIndex { + scrollPosition = .index(index: .message(index), position: .top(0.0), directionHint: .Up, animated: false, highlight: false, displayLink: false) + } + } + if let maxReadIndex = view.maxReadIndex, tagMask == nil, canScrollToRead { let aroundIndex = maxReadIndex scrollPosition = .unread(index: maxReadIndex) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 3ee90b13a9..bd66988300 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2536,7 +2536,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var transitionSourceHeight: CGFloat = 0.0 var transitionFraction: CGFloat = 0.0 - var transitionSourceAvatarFrame = CGRect() + var transitionSourceAvatarFrame: CGRect? var transitionSourceTitleFrame = CGRect() var transitionSourceSubtitleFrame = CGRect() @@ -2544,10 +2544,13 @@ final class PeerInfoHeaderNode: ASDisplayNode { let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor var effectiveSeparatorAlpha: CGFloat - if let navigationTransition = self.navigationTransition, let sourceAvatarNode = (navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode { + if let navigationTransition = self.navigationTransition { transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height transitionFraction = navigationTransition.fraction - transitionSourceAvatarFrame = sourceAvatarNode.view.convert(sourceAvatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) + + if let sourceAvatarNode = (navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode { + transitionSourceAvatarFrame = sourceAvatarNode.view.convert(sourceAvatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) + } transitionSourceTitleFrame = navigationTransition.sourceTitleFrame transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame @@ -2761,7 +2764,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.usernameNode.accessibilityLabel = usernameString.string let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize)) - let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY) + let avatarCenter: CGPoint + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY) + } else { + avatarCenter = avatarFrame.center + } let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size @@ -2893,7 +2901,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { let avatarScale: CGFloat let avatarOffset: CGFloat if self.navigationTransition != nil { - avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / avatarFrame.width + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { + avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / avatarFrame.width + } else { + avatarScale = 1.0 + } avatarOffset = 0.0 } else { avatarScale = 1.0 * (1.0 - titleCollapseFraction) + avatarMinScale * titleCollapseFraction @@ -2909,7 +2921,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isAvatarExpanded { self.avatarListNode.listContainerNode.isHidden = false - if !transitionSourceAvatarFrame.width.isZero { + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: transitionFraction * transitionSourceAvatarFrame.width / 2.0) transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: transitionFraction * transitionSourceAvatarFrame.width / 2.0) } else { @@ -2947,7 +2959,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isAvatarExpanded { let expandedAvatarCenter = CGPoint(x: expandedAvatarListSize.width / 2.0, y: expandedAvatarListSize.height / 2.0 - contentOffset / 2.0) apparentAvatarFrame = CGRect(origin: CGPoint(x: expandedAvatarCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize()) - if !transitionSourceAvatarFrame.width.isZero { + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { let expandedFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize) controlsClippingFrame = CGRect(origin: CGPoint(x: transitionFraction * transitionSourceAvatarFrame.minX + (1.0 - transitionFraction) * expandedFrame.minX, y: transitionFraction * transitionSourceAvatarFrame.minY + (1.0 - transitionFraction) * expandedFrame.minY), size: CGSize(width: transitionFraction * transitionSourceAvatarFrame.width + (1.0 - transitionFraction) * expandedFrame.width, height: transitionFraction * transitionSourceAvatarFrame.height + (1.0 - transitionFraction) * expandedFrame.height)) } else { @@ -2963,7 +2975,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let avatarListContainerFrame: CGRect let avatarListContainerScale: CGFloat if self.isAvatarExpanded { - if !transitionSourceAvatarFrame.width.isZero { + if let transitionSourceAvatarFrame = transitionSourceAvatarFrame { let neutralAvatarListContainerSize = expandedAvatarListSize let avatarListContainerSize = CGSize(width: neutralAvatarListContainerSize.width * (1.0 - transitionFraction) + transitionSourceAvatarFrame.width * transitionFraction, height: neutralAvatarListContainerSize.height * (1.0 - transitionFraction) + transitionSourceAvatarFrame.height * transitionFraction) avatarListContainerFrame = CGRect(origin: CGPoint(x: -avatarListContainerSize.width / 2.0, y: -avatarListContainerSize.height / 2.0), size: avatarListContainerSize) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index d51db1c7e0..51ffef6c3a 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -8548,7 +8548,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc override public var customNavigationData: CustomViewControllerNavigationData? { get { if !self.isSettings { - return ChatControllerNavigationData(peerId: self.peerId) + return ChatControllerNavigationData(peerId: self.peerId, threadId: self.chatLocation.threadId) } else { return nil } @@ -8760,7 +8760,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) } - if case .peer = self.chatLocation { + if self.chatLocation.peerId != nil { self.navigationBar?.makeCustomTransitionNode = { [weak self] other, isInteractive in guard let strongSelf = self else { return nil @@ -8777,9 +8777,6 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc if isInteractive && strongSelf.controllerNode.headerNode.isAvatarExpanded { return nil } - if other.contentNode != nil { - return nil - } if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() { return nil } @@ -8924,50 +8921,122 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } + static func displayChatNavigationMenu(context: AccountContext, chatNavigationStack: [ChatNavigationStackItem], nextFolderId: Int32?, parentController: ViewController, backButtonView: UIView, navigationController: NavigationController, gesture: ContextGesture) { + let peerMap = EngineDataMap( + Set(chatNavigationStack.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.Peer.init) + ) + let threadDataMap = EngineDataMap( + Set(chatNavigationStack.filter { $0.threadId != nil }).map { TelegramEngine.EngineData.Item.Peer.ThreadData(id: $0.peerId, threadId: $0.threadId!) } + ) + let _ = (context.engine.data.get( + peerMap, + threadDataMap + ) + |> deliverOnMainQueue).start(next: { [weak parentController, weak backButtonView, weak navigationController] peerMap, threadDataMap in + guard let parentController, let backButtonView else { + return + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let avatarSize = CGSize(width: 28.0, height: 28.0) + + var items: [ContextMenuItem] = [] + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatList_Tabs_AllChats, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) + }, action: { _, f in + f(.default) + + navigationController?.popToRoot(animated: true) + }))) + + for item in chatNavigationStack { + guard let maybeItemPeer = peerMap[item.peerId], let itemPeer = maybeItemPeer else { + continue + } + + let title: String + let iconSource: ContextMenuActionItemIconSource? + if let threadId = item.threadId { + guard let maybeThreadData = threadDataMap[TelegramEngine.EngineData.Item.Peer.ThreadData.Key(id: item.peerId, threadId: threadId)], let threadData = maybeThreadData else { + continue + } + title = threadData.info.title + iconSource = nil + } else { + if itemPeer.id == context.account.peerId { + title = presentationData.strings.DialogList_SavedMessages + iconSource = nil + } else { + title = itemPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + iconSource = ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: context.account, peer: itemPeer, size: avatarSize)) + } + } + + let isSavedMessages = itemPeer.id == context.account.peerId + + items.append(.action(ContextMenuActionItem(text: title, icon: { _ in + if isSavedMessages { + return generateAvatarImage(size: avatarSize, icon: savedMessagesIcon, iconScale: 0.5, color: .blue) + } + return nil + }, iconSource: iconSource, action: { _, f in + f(.default) + + guard let navigationController = navigationController else { + return + } + + var updatedChatNavigationStack = chatNavigationStack + if let index = updatedChatNavigationStack.firstIndex(of: item) { + updatedChatNavigationStack.removeSubrange(0 ..< (index + 1)) + } + + let navigateChatLocation: NavigateToChatControllerParams.Location + if let threadId = item.threadId { + navigateChatLocation = .replyThread(ChatReplyThreadMessage( + messageId: MessageId(peerId: item.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + )) + } else { + navigateChatLocation = .peer(itemPeer) + } + + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: navigateChatLocation, useBackAnimation: true, animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { _ in + })) + }))) + } + let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + parentController.presentInGlobalOverlay(contextController) + }) + } + override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - var chatNavigationStack: [PeerId] = [] + var chatNavigationStack: [ChatNavigationStackItem] = [] if !self.isSettings, let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary { chatNavigationStack.removeAll() - chatNavigationStack = summary.peerIds.filter({ $0 != peerId }) + chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: self.peerId, threadId: self.chatLocation.threadId) }) } if !chatNavigationStack.isEmpty { self.navigationBar?.backButtonNode.isGestureEnabled = true self.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in - guard let strongSelf = self else { + guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode, let navigationController = strongSelf.navigationController as? NavigationController else { gesture.cancel() return } - let _ = (strongSelf.context.engine.data.get(EngineDataList( - chatNavigationStack.map(TelegramEngine.EngineData.Item.Peer.Peer.init) - )) - |> deliverOnMainQueue).start(next: { peerList in - guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode else { - return - } - let peers = peerList.compactMap { $0 } - - let avatarSize = CGSize(width: 28.0, height: 28.0) - - var items: [ContextMenuItem] = [] - for peer in peers { - items.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), icon: { _ in return nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer, size: avatarSize)), action: { _, f in - f(.default) - - guard let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController else { - return - } - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), animated: true, completion: { _ in - })) - }))) - } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - strongSelf.presentInGlobalOverlay(contextController) - }) + PeerInfoScreenImpl.displayChatNavigationMenu( + context: strongSelf.context, + chatNavigationStack: chatNavigationStack, + nextFolderId: nil, + parentController: strongSelf, + backButtonView: backButtonNode.view, + navigationController: navigationController, + gesture: gesture + ) } } } @@ -9105,14 +9174,6 @@ private func getUserPeer(engine: TelegramEngine, peerId: EnginePeer.Id) -> Signa } } -final class PeerInfoNavigationSourceTag { - let peerId: PeerId - - init(peerId: PeerId) { - self.peerId = peerId - } -} - private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavigationTransitionNode { private let screenNode: PeerInfoScreenNode private let presentationData: PresentationData @@ -9238,7 +9299,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig transition.updateAlpha(node: currentBackButton, alpha: (1.0 - fraction)) } - if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let _ = (bottomNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode, let (previousTitleContainerNode, previousTitleNode) = self.previousTitleNode, let (previousStatusContainerNode, previousStatusNode) = self.previousStatusNode { + if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let (previousTitleContainerNode, previousTitleNode) = self.previousTitleNode, let (previousStatusContainerNode, previousStatusNode) = self.previousStatusNode { let previousTitleFrame = previousTitleView.titleContainerView.convert(previousTitleView.titleContainerView.bounds, to: bottomNavigationBar.view) let previousStatusFrame = previousTitleView.activityNode.view.convert(previousTitleView.activityNode.bounds, to: bottomNavigationBar.view)