diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index b1bee82fcc..fc3edc3ce2 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -1309,7 +1309,7 @@ private func mapPeersToFriends(accountId: AccountRecordId, accountPeerId: PeerId var profileImage: INImage? var isForum = false - if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) { + if let peer = peer as? TelegramChannel, peer.isForumOrMonoForum { isForum = true } diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 5247e3caca..37ca78b0d9 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -178,7 +178,7 @@ private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderC } var isForum = false - if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) { + if let peer = peer as? TelegramChannel, peer.isForumOrMonoForum { isForum = true } diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 8bc74ceefc..d6ac7578ed 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -110,7 +110,7 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En let clipStyle: AvatarNodeClipStyle if round { - if case let .channel(channel) = peer, channel.isForum { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index e5cebaf1af..2dfab03f46 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -195,7 +195,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch } var isForum = false - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 3a0b455703..6511928025 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1073,7 +1073,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - if openAsInlineForum, case let .channel(channel) = peer, channel.flags.contains(.isForum), threadId == nil { + if openAsInlineForum, case let .channel(channel) = peer, channel.isForum, threadId == nil { self.chatListDisplayNode.clearHighlightAnimated(true) if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) { self.setInlineChatList(location: nil) @@ -1083,7 +1083,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId { + if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( navigationController: navigationController, context: self.context, @@ -1093,7 +1093,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController channelMessageId: nil, isChannelPost: false, isForumPost: true, - isMonoforum: channel.flags.contains(.isMonoforum), + isMonoforumPost: channel.isMonoForum, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, @@ -1344,7 +1344,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case .chatList(.root) = strongSelf.location { navigationAnimationOptions = .removeOnMasterDetails } - if case let .channel(channel) = actualPeer, channel.flags.contains(.isForum), let threadId { + if case let .channel(channel) = actualPeer, channel.isForumOrMonoForum, let threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never).startStandalone() } else { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), purposefulAction: { @@ -1377,7 +1377,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case .chatList(.root) = strongSelf.location { navigationAnimationOptions = .removeOnMasterDetails } - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId { + if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never).startStandalone() } else { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), purposefulAction: { [weak self] in @@ -1515,11 +1515,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController switch item.index { case .chatList: - if case let .channel(channel) = peer.peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer.peer, channel.isForumOrMonoForum { if let threadId = threadId { let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) @@ -1588,7 +1588,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) @@ -1625,7 +1625,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) @@ -7163,7 +7163,7 @@ private final class ChatListLocationContext { self.ready.set(.single(true)) } - if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.flags.contains(.isForum) { + if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.isForumOrMonoForum { if let parentController = self.parentController, let navigationController = parentController.navigationController as? NavigationController { let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil) navigationController.replaceController(parentController, with: chatController, animated: true) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 9a884915cf..c44247d813 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -536,7 +536,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { if case let .forum(_, _, threadIdValue, _, _) = self.index { threadId = threadIdValue } - if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.flags.contains(.isForum) { + if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.isForumOrMonoForum { threadId = message.threadId } self.interaction.messageSelected(peer, threadId, message, peerData.promoInfo) @@ -768,7 +768,7 @@ private func leftRevealOptions(strings: PresentationStrings, theme: Presentation options.append(ItemListRevealOption(key: RevealOptionKey.toggleMarkedUnread.rawValue, title: strings.DialogList_Read, icon: readIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor)) } else { var canMarkUnread = true - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { canMarkUnread = false } @@ -1726,7 +1726,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { overrideImage = .deletedIcon } var isForumAvatar = false - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { isForumAvatar = true } if case let .peer(data) = item.content { @@ -2392,7 +2392,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } - if let _ = peerText, case let .channel(channel) = itemPeer.chatMainPeer, channel.flags.contains(.isForum), threadInfo == nil { + if let _ = peerText, case let .channel(channel) = itemPeer.chatMainPeer, channel.isForumOrMonoForum, threadInfo == nil { if let forumTopicData = forumTopicData { forumThread = (forumTopicData.id, forumTopicData.title, forumTopicData.iconFileId, forumTopicData.iconColor, forumTopicData.isUnread) } else if let threadInfo = threadInfo { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index b0d0a5b92a..80234f5e85 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -598,7 +598,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } var isForum = false - if let peer = chatPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if let peer = chatPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true if editing, case .chatList = mode { enabled = false @@ -948,7 +948,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } var isForum = false - if let peer = chatPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if let peer = chatPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true if editing, case .chatList = mode { enabled = false @@ -2987,7 +2987,7 @@ public final class ChatListNode: ListView { guard case .global = chatPeerId.category else { continue } - if case let .channel(channel) = peerMap[chatPeerId.peerId], channel.flags.contains(.isForum) { + if case let .channel(channel) = peerMap[chatPeerId.peerId], channel.isForumOrMonoForum { continue } itemId = ChatListNodePeerInputActivities.ItemId(peerId: chatPeerId.peerId, threadId: nil) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index dba6b75463..80c49a033a 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -1227,7 +1227,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { if case .app(true) = item.peerMode { clipStyle = .roundedRect displayDimensions = CGSize(width: displayDimensions.width, height: displayDimensions.width * 1.2) - } else if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + } else if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index b59c798b7e..0723c7d3a2 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -1721,7 +1721,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.avatarNode.imageNode.animateFirstTransition = item.animateFirstAvatarTransition var clipStyle: AvatarNodeClipStyle = .round - if case let .channel(channel) = item.peer, channel.isForum { + if case let .channel(channel) = item.peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index fe473f69e4..dc201ad059 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -556,7 +556,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { let avatarSize: CGFloat = 40.0 avatarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - avatarSize) / 2.0), y: floor((height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) var clipStyle: AvatarNodeClipStyle = .round - if case let .channel(channel) = iconPeer, channel.flags.contains(.isForum) { + if case let .channel(channel) = iconPeer, channel.isForumOrMonoForum { clipStyle = .roundedRect } var overrideImage: AvatarNodeImageOverride? diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 48e42c7283..9c93669319 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -203,7 +203,7 @@ public final class SelectablePeerNode: ASDisplayNode { } var isForum = false - if let peer = peer.chatMainPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if let peer = peer.chatMainPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true } @@ -375,7 +375,7 @@ public final class SelectablePeerNode: ASDisplayNode { } var isForum = false - if let peer = self.peer?.chatMainPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if let peer = self.peer?.chatMainPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true } diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index b3046db256..462c3a0737 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -576,7 +576,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate strongSelf.controllerInteraction!.selectedPeerIds.remove(peer.peerId) strongSelf.controllerInteraction!.selectedPeers = strongSelf.controllerInteraction!.selectedPeers.filter({ $0.peerId != peer.peerId }) } else { - if case let .channel(channel) = peer.peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer.peer, channel.isForumOrMonoForum { if strongSelf.controllerInteraction!.selectedTopics[peer.peerId] != nil { strongSelf.controllerInteraction!.selectedTopics[peer.peerId] = nil strongSelf.peersContentNode?.update() diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 2339fef17a..5aa75afbe9 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -465,7 +465,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1877932953] = { return Api.InputPrivacyRule.parse_inputPrivacyValueDisallowUsers($0) } dict[609840449] = { return Api.InputQuickReplyShortcut.parse_inputQuickReplyShortcut($0) } dict[18418929] = { return Api.InputQuickReplyShortcut.parse_inputQuickReplyShortcutId($0) } - dict[583071445] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) } + dict[-1334822736] = { return Api.InputReplyTo.parse_inputReplyToMessage($0) } + dict[1775660101] = { return Api.InputReplyTo.parse_inputReplyToMonoForum($0) } dict[1484862010] = { return Api.InputReplyTo.parse_inputReplyToStory($0) } dict[-251549057] = { return Api.InputSavedStarGift.parse_inputSavedStarGiftChat($0) } dict[545636920] = { return Api.InputSavedStarGift.parse_inputSavedStarGiftSlug($0) } diff --git a/submodules/TelegramApi/Sources/Api12.swift b/submodules/TelegramApi/Sources/Api12.swift index 6057c17d36..967020d8d7 100644 --- a/submodules/TelegramApi/Sources/Api12.swift +++ b/submodules/TelegramApi/Sources/Api12.swift @@ -272,14 +272,15 @@ public extension Api { } public extension Api { indirect enum InputReplyTo: TypeConstructorDescription { - case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?) + case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?, monoforumPeerId: Api.InputPeer?) + case inputReplyToMonoForum(monoforumPeerId: Api.InputPeer) case inputReplyToStory(peer: Api.InputPeer, storyId: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset): + case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset, let monoforumPeerId): if boxed { - buffer.appendInt32(583071445) + buffer.appendInt32(-1334822736) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(replyToMsgId, buffer: buffer, boxed: false) @@ -292,6 +293,13 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {monoforumPeerId!.serialize(buffer, true)} + break + case .inputReplyToMonoForum(let monoforumPeerId): + if boxed { + buffer.appendInt32(1775660101) + } + monoforumPeerId.serialize(buffer, true) break case .inputReplyToStory(let peer, let storyId): if boxed { @@ -305,8 +313,10 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset): - return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)]) + case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset, let monoforumPeerId): + return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any), ("monoforumPeerId", monoforumPeerId as Any)]) + case .inputReplyToMonoForum(let monoforumPeerId): + return ("inputReplyToMonoForum", [("monoforumPeerId", monoforumPeerId as Any)]) case .inputReplyToStory(let peer, let storyId): return ("inputReplyToStory", [("peer", peer as Any), ("storyId", storyId as Any)]) } @@ -331,6 +341,10 @@ public extension Api { } } var _7: Int32? if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() } + var _8: Api.InputPeer? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.InputPeer + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil @@ -338,8 +352,22 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7) + let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7, monoforumPeerId: _8) + } + else { + return nil + } + } + public static func parse_inputReplyToMonoForum(_ reader: BufferReader) -> InputReplyTo? { + var _1: Api.InputPeer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputPeer + } + let _c1 = _1 != nil + if _c1 { + return Api.InputReplyTo.inputReplyToMonoForum(monoforumPeerId: _1!) } else { return nil diff --git a/submodules/TelegramCallsUI/Sources/VideoChatExpandedSpeakingToastComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatExpandedSpeakingToastComponent.swift index d50ae11f7f..5b34902577 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatExpandedSpeakingToastComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatExpandedSpeakingToastComponent.swift @@ -144,7 +144,7 @@ final class VideoChatExpandedSpeakingToastComponent: Component { let avatarSize = CGSize(width: avatarWidth, height: avatarWidth) let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = component.peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = component.peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramCallsUI/Sources/VideoChatParticipantAvatarComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatParticipantAvatarComponent.swift index cab65d118b..ec8db9272d 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatParticipantAvatarComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatParticipantAvatarComponent.swift @@ -247,7 +247,7 @@ final class VideoChatParticipantAvatarComponent: Component { let avatarSize = availableSize let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = component.peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = component.peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 21d96b74db..65bfb08098 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -476,7 +476,7 @@ struct AccountMutableState { return peer.isForum } else if let chat = self.apiChats[peerId] { if let channel = parseTelegramGroupOrChannel(chat: chat) { - return channel.isForum + return channel.isForumOrMonoForum } else { return false } diff --git a/submodules/TelegramCore/Sources/Network/FetchV2.swift b/submodules/TelegramCore/Sources/Network/FetchV2.swift index 72703446ee..702c560994 100644 --- a/submodules/TelegramCore/Sources/Network/FetchV2.swift +++ b/submodules/TelegramCore/Sources/Network/FetchV2.swift @@ -103,6 +103,10 @@ private final class FetchImpl { init(range: Range) { self.range = range } + + deinit { + self.disposable?.dispose() + } } private final class HashRangeData { diff --git a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift index edf7c27b25..eaf279d18e 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift @@ -211,7 +211,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, if let result = result { return postbox.transaction { transaction -> RequestEditMessageResult in var toMedia: Media? - if let message = result.messages.first.flatMap({ StoreMessage(apiMessage: $0, accountPeerId: accountPeerId, peerIsForum: peer.isForum) }) { + if let message = result.messages.first.flatMap({ StoreMessage(apiMessage: $0, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) }) { toMedia = message.media.first } @@ -227,7 +227,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, let peers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: peers) - if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum), case let .Id(id) = message.id { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum), case let .Id(id) = message.id { transaction.updateMessage(id, update: { previousMessage in var updatedFlags = message.flags var updatedLocalTags = message.localTags diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 4fac57c73d..057b989653 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -324,8 +324,18 @@ private func sendUploadedMessageContent( var uniqueId: Int64 = 0 var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? - var replyMessageId: Int32? = threadId.flatMap { threadId in - return Int32(clamping: threadId) + var replyMessageId: Int32? + var topMsgId: Int32? + var monoforumPeerId: Api.InputPeer? + if let threadId { + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + if let monoforumTargetPeer = transaction.getPeer(PeerId(threadId)) { + monoforumPeerId = apiInputPeer(monoforumTargetPeer) + } + } else { + replyMessageId = Int32(clamping: threadId) + topMsgId = Int32(clamping: threadId) + } } var replyToStoryId: StoryId? var scheduleTime: Int32? @@ -412,19 +422,24 @@ private func sendUploadedMessageContent( } var replyTo: Api.InputReplyTo? - if let replyMessageId = replyMessageId { + if let replyMessageId { flags |= 1 << 0 var replyFlags: Int32 = 0 - if threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) - } else if let replyToStoryId = replyToStoryId { + if monoforumPeerId != nil { + replyFlags |= 1 << 5 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) + } else if let replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } + } else if let monoforumPeerId { + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars), info: .acknowledgement, tag: dependencyTag) @@ -438,10 +453,13 @@ private func sendUploadedMessageContent( flags |= 1 << 0 var replyFlags: Int32 = 0 - if threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + if monoforumPeerId != nil { + replyFlags |= 1 << 5 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -452,10 +470,8 @@ private func sendUploadedMessageContent( sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars), tag: dependencyTag) |> map(NetworkRequestResult.result) case let .forward(sourceInfo): - var topMsgId: Int32? - if let threadId = threadId { + if topMsgId != nil { flags |= Int32(1 << 9) - topMsgId = Int32(clamping: threadId) } if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) { @@ -474,10 +490,13 @@ private func sendUploadedMessageContent( flags |= 1 << 0 var replyFlags: Int32 = 0 - if threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + if monoforumPeerId != nil { + replyFlags |= 1 << 5 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -492,18 +511,18 @@ private func sendUploadedMessageContent( if let replyMessageId = replyMessageId { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } sendMessageRequest = network.request(Api.functions.messages.sendScreenshotNotification(peer: inputPeer, replyTo: replyTo, randomId: uniqueId)) @@ -641,7 +660,6 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M flags |= 1 << 21 } - let sendMessageRequest: Signal switch content { case let .text(text): @@ -650,7 +668,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M flags |= 1 << 0 let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -658,7 +676,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M } } else if let threadId { flags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars)) @@ -671,7 +689,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M flags |= 1 << 0 let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -679,7 +697,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M } } else if let threadId { flags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars)) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 126edbe4a4..4c108d8c83 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1571,10 +1571,11 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: case let .draftMessage(_, replyToMsgHeader, message, entities, media, date, messageEffectId): let _ = media var replySubject: EngineMessageReplySubject? - if let replyToMsgHeader = replyToMsgHeader { + if let replyToMsgHeader { switch replyToMsgHeader { - case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities, quoteOffset): + case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities, quoteOffset, monoforumPeerId): let _ = topMsgId + let _ = monoforumPeerId var quote: EngineMessageReplyQuote? if let quoteText = quoteText { @@ -1612,6 +1613,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: ) case .inputReplyToStory: break + case .inputReplyToMonoForum: + break } } inputState = SynchronizeableChatInputState(replySubject: replySubject, text: message, entities: messageTextEntitiesFromApiEntities(entities ?? []), timestamp: date, textSelection: nil, messageEffectId: messageEffectId) @@ -1926,8 +1929,10 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, network: Netwo case let .AddMessages(messages, _): for message in messages { if let threadId = message.threadId { - if let channel = state.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { - forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + if let channel = state.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { + if channel.flags.contains(.isForum) { + forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + } } } } diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 569a026238..0e11587c6e 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -125,7 +125,7 @@ private func fetchWebpage(account: Account, messageId: MessageId, threadId: Int6 let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for message in messages { - if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum, namespace: targetMessageNamespace) { + if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum, namespace: targetMessageNamespace) { var webpage: TelegramMediaWebpage? for media in storeMessage.media { if let media = media as? TelegramMediaWebpage { @@ -1110,7 +1110,7 @@ public final class AccountViewTracker { updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) for message in messages { - guard let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForum) else { + guard let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForumOrMonoForum) else { continue } guard case let .Id(id) = storeMessage.id else { diff --git a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift index b84f7f2577..d0b2dc5fa0 100644 --- a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift @@ -145,7 +145,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes namespace = Namespaces.Message.ScheduledCloud } - if let apiMessage = apiMessage, let apiMessagePeerId = apiMessage.peerId, let updatedMessage = StoreMessage(apiMessage: apiMessage, accountPeerId: accountPeerId, peerIsForum: transaction.getPeer(apiMessagePeerId)?.isForum ?? false, namespace: namespace) { + if let apiMessage = apiMessage, let apiMessagePeerId = apiMessage.peerId, let updatedMessage = StoreMessage(apiMessage: apiMessage, accountPeerId: accountPeerId, peerIsForum: transaction.getPeer(apiMessagePeerId)?.isForumOrMonoForum ?? false, namespace: namespace) { media = updatedMessage.media attributes = updatedMessage.attributes text = updatedMessage.text @@ -423,7 +423,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage for apiMessage in result.messages { var peerIsForum = false if let apiMessagePeerId = apiMessage.peerId, let peer = transaction.getPeer(apiMessagePeerId) { - if peer.isForum { + if peer.isForumOrMonoForum { peerIsForum = true } } diff --git a/submodules/TelegramCore/Sources/State/FetchChatList.swift b/submodules/TelegramCore/Sources/State/FetchChatList.swift index 587d613e1d..5501c844c6 100644 --- a/submodules/TelegramCore/Sources/State/FetchChatList.swift +++ b/submodules/TelegramCore/Sources/State/FetchChatList.swift @@ -145,7 +145,7 @@ private func parseDialogs(accountPeerId: PeerId, apiDialogs: [Api.Dialog], apiMe for message in apiMessages { var peerIsForum = false - if let peerId = message.peerId, let peer = peers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = peers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index 4290ad04f4..60a87f252f 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -763,7 +763,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran var storeMessages: [StoreMessage] = [] for message in messages { - if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForum, namespace: messageNamespace) { + if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForumOrMonoForum, namespace: messageNamespace) { var attributes = storeMessage.attributes if let channelPts = channelPts { attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) @@ -810,7 +810,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran } var ids = Set() for message in apiMessages { - if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForum, namespace: messageNamespace), case let .Id(id) = parsedMessage.id { + if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForumOrMonoForum, namespace: messageNamespace), case let .Id(id) = parsedMessage.id { if let tag = tag { if parsedMessage.tags.contains(tag) { ids.insert(id) @@ -1014,7 +1014,7 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa var storeMessages: [StoreMessage] = [] for message in messages { - if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForum, namespace: messageNamespace) { + if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForumOrMonoForum, namespace: messageNamespace) { var attributes = storeMessage.attributes if let channelPts = channelPts { attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) @@ -1059,7 +1059,7 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa } var ids = Set() for message in apiMessages { - if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForum, namespace: messageNamespace), case let .Id(id) = parsedMessage.id { + if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: topPeer.isForumOrMonoForum, namespace: messageNamespace), case let .Id(id) = parsedMessage.id { ids.insert(id) } } diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index 54b25a0bfb..44c5eca149 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -301,7 +301,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis for (peer, messages, chats, users) in results { if !messages.isEmpty { for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) { additionalMessages.append(message) } } @@ -905,7 +905,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH var storeMessages: [StoreMessage] = [] for message in messages { - if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum, namespace: namespace) { + if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum, namespace: namespace) { if let channelPts = channelPts { var attributes = storeMessage.attributes attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) @@ -1204,7 +1204,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { diff --git a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift index ba4ec8405c..1f884ea498 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift @@ -534,7 +534,7 @@ func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Net private func synchronizeMessageHistoryTagSummary(accountPeerId: PeerId, postbox: Postbox, network: Network, entry: InvalidatedMessageHistoryTagsSummaryEntry) -> Signal { return postbox.transaction { transaction -> Signal in if let threadId = entry.key.threadId { - if let peer = transaction.getPeer(entry.key.peerId) as? TelegramChannel, peer.flags.contains(.isForum), let inputPeer = apiInputPeer(peer) { + if let peer = transaction.getPeer(entry.key.peerId) as? TelegramChannel, peer.flags.contains(.isForum), !peer.flags.contains(.isMonoforum), let inputPeer = apiInputPeer(peer) { return network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: threadId), offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift index 3277233f4b..fcb961b8f3 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift @@ -128,7 +128,7 @@ func managedSynchronizeChatInputStateOperations(postbox: Postbox, network: Netwo private func synchronizeChatInputState(transaction: Transaction, postbox: Postbox, network: Network, peerId: PeerId, threadId: Int64?, operation: SynchronizeChatInputStateOperation) -> Signal { var inputState: SynchronizeableChatInputState? let peerChatInterfaceState: StoredPeerChatInterfaceState? - if let threadId = threadId { + if let threadId { peerChatInterfaceState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId) } else { peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId) @@ -146,8 +146,13 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo } } var topMsgId: Int32? - if let threadId = threadId { - topMsgId = Int32(clamping: threadId) + var monoforumPeerId: Api.InputPeer? + if let threadId { + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + monoforumPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } else { + topMsgId = Int32(clamping: threadId) + } } var replyTo: Api.InputReplyTo? @@ -155,7 +160,12 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo flags |= 1 << 0 var innerFlags: Int32 = 0 - //inputReplyToMessage#73ec805 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector = InputReplyTo; + if topMsgId != nil { + innerFlags |= 1 << 0 + } else if monoforumPeerId != nil { + innerFlags |= 1 << 5 + } + var replyToPeer: Api.InputPeer? var discard = false if replySubject.messageId.peerId != peerId { @@ -201,14 +211,17 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo } if !discard { - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId) } - } else if let topMsgId = topMsgId { + } else if let topMsgId { flags |= 1 << 0 var innerFlags: Int32 = 0 innerFlags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: topMsgId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: topMsgId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) + } else if let monoforumPeerId { + flags |= 1 << 0 + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } return network.request(Api.functions.messages.saveDraft(flags: flags, replyTo: replyTo, peer: inputPeer, message: inputState?.text ?? "", entities: apiEntitiesFromMessageTextEntities(inputState?.entities ?? [], associatedPeers: SimpleDictionary()), media: nil, effect: nil)) diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift index e71888abe3..f0dd1edf10 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift @@ -195,7 +195,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox, for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { @@ -318,7 +318,7 @@ private func synchronizePinnedSavedChats(transaction: Transaction, postbox: Post for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 41a290a289..20f4471981 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -1035,9 +1035,14 @@ public final class PendingMessageManager { } var topMsgId: Int32? + var monoforumPeerId: Api.InputPeer? if let threadId = messages[0].0.threadId { - flags |= Int32(1 << 9) - topMsgId = Int32(clamping: threadId) + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + monoforumPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } else { + flags |= Int32(1 << 9) + topMsgId = Int32(clamping: threadId) + } } var replyTo: Api.InputReplyTo? @@ -1047,6 +1052,8 @@ public final class PendingMessageManager { var replyFlags: Int32 = 0 if topMsgId != nil { replyFlags |= 1 << 0 + } else if monoforumPeerId != nil { + replyFlags |= 1 << 5 } var replyToPeerId: Api.InputPeer? @@ -1085,12 +1092,15 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) - } else if let replyToStoryId = replyToStoryId { + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId) + } else if let replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } + } else if let monoforumPeerId { + flags |= 1 << 0 + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } var quickReplyShortcut: Api.InputQuickReplyShortcut? @@ -1328,14 +1338,20 @@ public final class PendingMessageManager { var sendAsPeerId: PeerId? var bubbleUpEmojiOrStickersets = false var quickReply: OutgoingQuickReplyMessageAttribute? - var suggestedPost: OutgoingSuggestedPostMessageAttribute? var messageEffect: EffectMessageAttribute? var allowPaidStars: Int64? var flags: Int32 = 0 - //TODO:release - let _ = suggestedPost + var topMsgId: Int32? + var monoforumPeerId: Api.InputPeer? + if let threadId = message.threadId { + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + monoforumPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } else { + topMsgId = Int32(clamping: threadId) + } + } for attribute in message.attributes { if let replyAttribute = attribute as? ReplyMessageAttribute { @@ -1370,8 +1386,6 @@ public final class PendingMessageManager { sendAsPeerId = attribute.peerId } else if let attribute = attribute as? OutgoingQuickReplyMessageAttribute { quickReply = attribute - } else if let attribute = attribute as? OutgoingSuggestedPostMessageAttribute { - suggestedPost = attribute } else if let attribute = attribute as? EffectMessageAttribute { messageEffect = attribute } else if let attribute = attribute as? ForwardVideoTimestampAttribute { @@ -1413,8 +1427,10 @@ public final class PendingMessageManager { flags |= 1 << 0 var replyFlags: Int32 = 0 - if message.threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 + } else if monoforumPeerId != nil { + replyFlags |= 1 << 5 } var replyToPeerId: Api.InputPeer? @@ -1453,12 +1469,17 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) + + + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } + } else if let monoforumPeerId { + flags |= 1 << 0 + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } if let attribute = message.webpagePreviewAttribute { if attribute.leadingPreview { @@ -1500,8 +1521,10 @@ public final class PendingMessageManager { flags |= 1 << 0 var replyFlags: Int32 = 0 - if message.threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 + } else if monoforumPeerId != nil { + replyFlags |= 1 << 5 } var replyToPeerId: Api.InputPeer? @@ -1540,12 +1563,15 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } + } else if let monoforumPeerId { + flags |= 1 << 0 + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } if let attribute = message.webpagePreviewAttribute { @@ -1620,8 +1646,10 @@ public final class PendingMessageManager { flags |= 1 << 0 var replyFlags: Int32 = 0 - if message.threadId != nil { + if topMsgId != nil { replyFlags |= 1 << 0 + } else if monoforumPeerId != nil { + replyFlags |= 1 << 5 } var replyToPeerId: Api.InputPeer? @@ -1660,12 +1688,15 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } + } else if let monoforumPeerId { + flags |= 1 << 0 + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } var quickReplyShortcut: Api.InputQuickReplyShortcut? @@ -1689,18 +1720,18 @@ public final class PendingMessageManager { if let replyMessageId = replyMessageId { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 replyTo = .inputReplyToStory(peer: inputPeer, storyId: replyToStoryId.id) } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) } } else { let replyFlags: Int32 = 0 - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: 0, topMsgId: nil, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) } sendMessageRequest = network.request(Api.functions.messages.sendScreenshotNotification(peer: inputPeer, replyTo: replyTo, randomId: uniqueId)) diff --git a/submodules/TelegramCore/Sources/State/ResetState.swift b/submodules/TelegramCore/Sources/State/ResetState.swift index c56a33721a..a91214f6e4 100644 --- a/submodules/TelegramCore/Sources/State/ResetState.swift +++ b/submodules/TelegramCore/Sources/State/ResetState.swift @@ -25,7 +25,7 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer } if peerId.namespace == Namespaces.Peer.CloudChannel { - if let channel = transaction.getPeer(peerId) as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = transaction.getPeer(peerId) as? TelegramChannel, channel.isForumOrMonoForum { transaction.setPeerPinnedThreads(peerId: peerId, threadIds: []) for threadId in transaction.setMessageHistoryThreads(peerId: peerId) { transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: nil) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index bb0bc2a6d4..e77b74f5fa 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -66,11 +66,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { case .broadcast: return .channel case .group: - if channel.flags.contains(.isForum) { - return .group - } else { - return .group - } + return .group } } else { assertionFailure() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index a5295b2a17..57056ba7c0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -116,6 +116,7 @@ func _internal_togglePeerUnreadMarkInteractively(postbox: Postbox, network: Netw } func _internal_markForumThreadAsReadInteractively(transaction: Transaction, network: Network, viewTracker: AccountViewTracker, peerId: PeerId, threadId: Int64) { + //TODO:release monoforums guard let peer = transaction.getPeer(peerId) else { return } @@ -145,6 +146,7 @@ func _internal_markForumThreadAsReadInteractively(transaction: Transaction, netw } func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, network: Network, viewTracker: AccountViewTracker, peerId: PeerId, setToValue: Bool? = nil) { + //TODO:release monoforums guard let peer = transaction.getPeer(peerId) else { return } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift index d1df9120dc..7c5523e029 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift @@ -147,16 +147,20 @@ public enum RequestWebViewError { case generic } -private func keepWebViewSignal(network: Network, stateManager: AccountStateManager, flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMessageId: MessageId?, threadId: Int64?, sendAs: Api.InputPeer?) -> Signal { +private func keepWebViewSignal(network: Network, stateManager: AccountStateManager, flags: Int32, peer: Api.InputPeer, monoforumPeerId: Api.InputPeer?, bot: Api.InputUser, queryId: Int64, replyToMessageId: MessageId?, threadId: Int64?, sendAs: Api.InputPeer?) -> Signal { let signal = Signal { subscriber in let poll = Signal { subscriber in var replyTo: Api.InputReplyTo? - if let replyToMessageId = replyToMessageId { + if let replyToMessageId { var replyFlags: Int32 = 0 - if threadId != nil { + var topMsgId: Int32? + if monoforumPeerId != nil { + replyFlags |= 1 << 5 + } else if let threadId { replyFlags |= 1 << 0 + topMsgId = Int32(clamping: threadId) } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) } let signal: Signal = network.request(Api.functions.messages.prolongWebView(flags: flags, peer: peer, bot: bot, queryId: queryId, replyTo: replyTo, sendAs: sendAs)) |> mapError { _ -> KeepWebViewError in @@ -223,14 +227,30 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager: } var replyTo: Api.InputReplyTo? + + var monoforumPeerId: Api.InputPeer? + var topMsgId: Int32? + if let threadId { + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + monoforumPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } else { + topMsgId = Int32(clamping: threadId) + } + } + if let replyToMessageId = replyToMessageId { flags |= (1 << 0) var replyFlags: Int32 = 0 - if threadId != nil { + + if monoforumPeerId != nil { + replyFlags |= 1 << 5 + } else if topMsgId != nil { replyFlags |= 1 << 0 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: threadId.flatMap(Int32.init(clamping:)), replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyToMessageId.id, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId) + } else if let monoforumPeerId { + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform, replyTo: replyTo, sendAs: nil)) @@ -249,7 +269,7 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager: } let keepAlive: Signal? if let queryId { - keepAlive = keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId, threadId: threadId, sendAs: nil) + keepAlive = keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, monoforumPeerId: monoforumPeerId, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId, threadId: threadId, sendAs: nil) } else { keepAlive = nil } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index 34b3779c6b..ab96357d77 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -522,7 +522,7 @@ extension EngineChatList.Item { let readCounters = readState.flatMap(EnginePeerReadCounters.init) if let channel = renderedPeer.peer as? TelegramChannel { - if channel.flags.contains(.isForum) { + if channel.isForumOrMonoForum { draft = nil } else { forumTopicDataValue = nil diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift index 558e7408b9..abf99a1e49 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift @@ -38,17 +38,25 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, if let peer = transaction.getPeer(key.peerId), let inputPeer = apiInputPeer(peer) { var topMsgId: Int32? + var monoforumPeerId: Api.InputPeer? if let threadId = key.threadId { - topMsgId = Int32(clamping: threadId) + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + monoforumPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } else { + topMsgId = Int32(clamping: threadId) + } } var flags: Int32 = 0 var replyTo: Api.InputReplyTo? - if let topMsgId = topMsgId { + if let topMsgId { flags |= (1 << 0) var innerFlags: Int32 = 0 innerFlags |= 1 << 0 - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: 0, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: 0, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil) + } else if let monoforumPeerId { + flags |= (1 << 0) + replyTo = .inputReplyToMonoForum(monoforumPeerId: monoforumPeerId) } signals.append(network.request(Api.functions.messages.saveDraft(flags: flags, replyTo: replyTo, peer: inputPeer, message: "", entities: nil, media: nil, effect: nil)) |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift index 6d54271de4..48d68930b9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift @@ -100,7 +100,7 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po var storeMessages: [StoreMessage] = [] for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) { storeMessages.append(message) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 093db67ef1..c183bfa364 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -8,6 +8,7 @@ private struct DiscussionMessage { var channelMessageId: MessageId? var isChannelPost: Bool var isForumPost: Bool + var isMonoforumPost: Bool var maxMessage: MessageId? var maxReadIncomingMessageId: MessageId? var maxReadOutgoingMessageId: MessageId? @@ -165,7 +166,7 @@ private class ReplyThreadHistoryContextImpl { switch discussionMessage { case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, unreadCount, chats, users): let parsedMessages = messages.compactMap { message -> StoreMessage? in - StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) + StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) } guard let topMessage = parsedMessages.last, let parsedIndex = topMessage.index else { @@ -237,8 +238,14 @@ private class ReplyThreadHistoryContextImpl { } var isForumPost = false - if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel, channel.flags.contains(.isForum) { - isForumPost = true + var isMonoforumPost = false + if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel { + if channel.isForumOrMonoForum { + isForumPost = true + } + if channel.isMonoForum { + isMonoforumPost = true + } } return .single(DiscussionMessage( @@ -246,6 +253,7 @@ private class ReplyThreadHistoryContextImpl { channelMessageId: channelMessageId, isChannelPost: isChannelPost, isForumPost: isForumPost, + isMonoforumPost: isMonoforumPost, maxMessage: resolvedMaxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in @@ -565,7 +573,7 @@ public struct ChatReplyThreadMessage: Equatable { public var channelMessageId: MessageId? public var isChannelPost: Bool public var isForumPost: Bool - public var isMonoforum: Bool + public var isMonoforumPost: Bool public var maxMessage: MessageId? public var maxReadIncomingMessageId: MessageId? public var maxReadOutgoingMessageId: MessageId? @@ -582,13 +590,13 @@ public struct ChatReplyThreadMessage: Equatable { } } - public init(peerId: PeerId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, isMonoforum: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) { + public init(peerId: PeerId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, isMonoforumPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) { self.peerId = peerId self.threadId = threadId self.channelMessageId = channelMessageId self.isChannelPost = isChannelPost self.isForumPost = isForumPost - self.isMonoforum = isMonoforum + self.isMonoforumPost = isMonoforumPost self.maxMessage = maxMessage self.maxReadIncomingMessageId = maxReadIncomingMessageId self.maxReadOutgoingMessageId = maxReadOutgoingMessageId @@ -600,7 +608,7 @@ public struct ChatReplyThreadMessage: Equatable { public var normalized: ChatReplyThreadMessage { if self.isForumPost { - return ChatReplyThreadMessage(peerId: self.peerId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) + return ChatReplyThreadMessage(peerId: self.peerId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: self.isMonoforumPost, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) } else { return self } @@ -644,7 +652,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa switch discussionMessage { case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, unreadCount, chats, users): let parsedMessages = messages.compactMap { message -> StoreMessage? in - StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) + StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) } guard let topMessage = parsedMessages.last, let parsedIndex = topMessage.index else { @@ -685,8 +693,14 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } var isForumPost = false - if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel, channel.flags.contains(.isForum) { - isForumPost = true + var isMonoforumPost = false + if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel { + if channel.isForumOrMonoForum { + isForumPost = true + } + if channel.isMonoForum { + isMonoforumPost = true + } } return DiscussionMessage( @@ -694,6 +708,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa channelMessageId: channelMessageId, isChannelPost: isChannelPost, isForumPost: isForumPost, + isMonoforumPost: isMonoforumPost, maxMessage: resolvedMaxMessage, maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId) @@ -718,6 +733,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa channelMessageId: nil, isChannelPost: false, isForumPost: true, + isMonoforumPost: false, maxMessage: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxKnownMessageId), maxReadIncomingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxIncomingReadId), maxReadOutgoingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxOutgoingReadId), @@ -933,7 +949,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa channelMessageId: discussionMessage.channelMessageId, isChannelPost: discussionMessage.isChannelPost, isForumPost: discussionMessage.isForumPost, - isMonoforum: false, + isMonoforumPost: discussionMessage.isMonoforumPost, maxMessage: discussionMessage.maxMessage, maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId, maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index ff1dfd86c7..0f662c36e9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -139,7 +139,7 @@ private func mergedState(transaction: Transaction, seedConfiguration: SeedConfig var renderedMessages: [Message] = [] for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = peers[peerId], peer.isForum { + if let peerId = message.peerId, let peer = peers[peerId], peer.isForumOrMonoForum { peerIsForum = true } if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { @@ -671,7 +671,7 @@ func _internal_downloadMessage(accountPeerId: PeerId, postbox: Postbox, network: var renderedMessages: [Message] = [] for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { renderedMessages.append(renderedMessage) } } @@ -755,7 +755,7 @@ func fetchRemoteMessage(accountPeerId: PeerId, postbox: Postbox, source: FetchMe var renderedMessages: [Message] = [] for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = transaction.getPeer(peerId) ?? parsedPeers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = transaction.getPeer(peerId) ?? parsedPeers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum, namespace: id.namespace), case let .Id(updatedId) = message.id { @@ -810,7 +810,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre messages = [] } for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForumOrMonoForum) { return message.index } } @@ -847,7 +847,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre messages = [] } for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForumOrMonoForum) { return message.index } } @@ -880,7 +880,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre messages = [] } for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: secondaryPeer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: secondaryPeer.isForumOrMonoForum) { return message.index } } @@ -904,7 +904,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre messages = [] } for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForumOrMonoForum) { return message.index } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index 9aa4fff9e3..4efa8be0e1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -842,7 +842,7 @@ public final class SparseMessageCalendar { let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for message in messages { - if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) { + if let parsedMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum) { parsedMessages.append(parsedMessage) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift index 1c9193d8bf..2282e190f1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift @@ -882,7 +882,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, var storeMessages: [StoreMessage] = [] for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForumOrMonoForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift index 0dce03b35c..6d37d15ec6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift @@ -114,7 +114,7 @@ func _internal_requestPeerPhotos(accountPeerId: PeerId, postbox: Postbox, networ var renderedMessages: [Message] = [] for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForumOrMonoForum), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { renderedMessages.append(renderedMessage) } } diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index a2a3cc0197..f2fcf59926 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -271,6 +271,22 @@ public extension Peer { } } + var isMonoForum: Bool { + if let channel = self as? TelegramChannel { + return channel.flags.contains(.isMonoforum) + } else { + return false + } + } + + var isForumOrMonoForum: Bool { + if let channel = self as? TelegramChannel { + return channel.flags.contains(.isForum) || channel.flags.contains(.isMonoforum) + } else { + return false + } + } + var nameColor: PeerNameColor? { switch self { case let user as TelegramUser: diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsPeerComponent.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsPeerComponent.swift index c6759c8c06..9ef7002203 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsPeerComponent.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsPeerComponent.swift @@ -206,7 +206,7 @@ final class AdminUserActionsPeerComponent: Component { } if let peer = component.peer { let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index 5caccf8eb1..7a82d4ddde 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -461,7 +461,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { if self.discussButton.isHidden { if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel { - if case .broadcast = peer.info, interfaceState.starGiftsAvailable { + if case let .broadcast(broadcastInfo) = peer.info, interfaceState.starGiftsAvailable { if self.giftButton.isHidden && !isFirstTime { self.giftButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.giftButton.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2) @@ -469,7 +469,12 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { self.giftButton.isHidden = false self.helpButton.isHidden = true - self.suggestedPostButton.isHidden = true + self.suggestedPostButton.isHidden = !broadcastInfo.flags.contains(.hasMonoforum) + self.presentGiftOrSuggestTooltip() + } else if case let .broadcast(broadcastInfo) = peer.info, broadcastInfo.flags.contains(.hasMonoforum) { + self.giftButton.isHidden = true + self.helpButton.isHidden = true + self.suggestedPostButton.isHidden = false self.presentGiftOrSuggestTooltip() } else if peer.flags.contains(.isGigagroup), self.action == .muteNotifications || self.action == .unmuteNotifications { self.giftButton.isHidden = true diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index dd2809fc38..da52aa556f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1161,7 +1161,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } var hasReply = replyMessage != nil || replyForward != nil || replyStory != nil - if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil { + if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum, item.message.associatedThreadInfo != nil { if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId { hasReply = false } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index a998ec96ff..9154d474d5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2227,7 +2227,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if replyMessage != nil || replyForward != nil || replyStory != nil { displayHeader = true } - if !displayHeader, case .peer = item.chatLocation, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil { + if !displayHeader, case .peer = item.chatLocation, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum, item.message.associatedThreadInfo != nil { displayHeader = true } if case let .customChatContents(contents) = item.associatedData.subject, case .hashTagSearch = contents.kind, let peer = item.message.peers[item.message.id.peerId] { @@ -2572,7 +2572,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var hasThreadInfo = false - if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1 || item.associatedData.isRecentActions), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil { + if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1 || item.associatedData.isRecentActions), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum, item.message.associatedThreadInfo != nil { hasThreadInfo = true } else if case let .customChatContents(contents) = item.associatedData.subject, case .hashTagSearch = contents.kind { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index b76046a703..83bb0ae9c4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -93,7 +93,7 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs } var sameThread = true - if let lhsPeer = lhs.peers[lhs.id.peerId], let rhsPeer = rhs.peers[rhs.id.peerId], arePeersEqual(lhsPeer, rhsPeer), let channel = lhsPeer as? TelegramChannel, channel.flags.contains(.isForum), lhs.threadId != rhs.threadId { + if let lhsPeer = lhs.peers[lhs.id.peerId], let rhsPeer = rhs.peers[rhs.id.peerId], arePeersEqual(lhsPeer, rhsPeer), let channel = lhsPeer as? TelegramChannel, channel.isForumOrMonoForum, lhs.threadId != rhs.threadId { sameThread = false } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 587acc94ba..a2228c18aa 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -727,7 +727,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { } var hasReply = replyMessage != nil || replyForward != nil || replyStory != nil - if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), item.message.associatedThreadInfo != nil { + if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum, item.message.associatedThreadInfo != nil { if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId { hasReply = false } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode/Sources/ChatMessageThreadInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode/Sources/ChatMessageThreadInfoNode.swift index eb2b0893f6..0421d39818 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode/Sources/ChatMessageThreadInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode/Sources/ChatMessageThreadInfoNode.swift @@ -323,7 +323,7 @@ public class ChatMessageThreadInfoNode: ASDisplayNode { var topicTitle = "" var topicIconId: Int64? var topicIconColor: Int32 = 0 - if let _ = arguments.parentMessage.threadId, let channel = arguments.parentMessage.peers[arguments.parentMessage.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), let threadInfo = arguments.parentMessage.associatedThreadInfo { + if let _ = arguments.parentMessage.threadId, let channel = arguments.parentMessage.peers[arguments.parentMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum, let threadInfo = arguments.parentMessage.associatedThreadInfo { topicTitle = threadInfo.title topicIconId = threadInfo.icon topicIconColor = threadInfo.iconColor diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/BUILD b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/BUILD index 85a972457e..0a13c0f284 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/BUILD @@ -23,6 +23,7 @@ swift_library( "//submodules/Components/BlurredBackgroundComponent", "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/Components/BundleIconComponent", + "//submodules/AvatarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift index 0964fd1ec4..88cc604f04 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift @@ -13,6 +13,7 @@ import AccountContext import BlurredBackgroundComponent import EmojiStatusComponent import BundleIconComponent +import AvatarNode public final class ChatSidePanelEnvironment: Equatable { public let insets: UIEdgeInsets @@ -111,6 +112,7 @@ public final class ChatSideTopicsPanel: Component { private let containerButton: HighlightTrackingButton private let icon = ComponentView() + private var avatarNode: AvatarNode? private let title = ComponentView() init(context: AccountContext, action: @escaping (() -> Void), contextGesture: @escaping (ContextGesture, ContextExtractedContentContainingNode) -> Void) { @@ -204,7 +206,7 @@ public final class ChatSideTopicsPanel: Component { containerSize: CGSize(width: 30.0, height: 30.0) ) - let titleText: String = item.item.threadData?.info.title ?? "Topic" + let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " " let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( @@ -228,6 +230,33 @@ public final class ChatSideTopicsPanel: Component { self.containerButton.addSubview(iconView) } iconView.frame = iconFrame + + if "".isEmpty { + iconView.isHidden = true + + let avatarNode: AvatarNode + if let current = self.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 11.0)) + self.avatarNode = avatarNode + self.containerButton.addSubview(avatarNode.view) + } + avatarNode.frame = iconFrame + avatarNode.updateSize(size: iconFrame.size) + + if let peer = item.item.renderedPeer.chatMainPeer { + if peer.smallProfileImage != nil { + avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) + } else { + avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) + } + } + } else if let avatarNode = self.avatarNode { + self.avatarNode = nil + avatarNode.view.removeFromSuperview() + iconView.isHidden = false + } } if let titleView = self.title.view { @@ -572,123 +601,54 @@ public final class ChatSideTopicsPanel: Component { self.state = state if self.component == nil { - let viewKey: PostboxViewKey = .messageHistoryThreadIndex( - id: component.peerId, - summaryComponents: ChatListEntrySummaryComponents( - components: [ - ChatListEntryMessageTagSummaryKey( - tag: .unseenPersonalMessage, - actionType: PendingMessageActionType.consumeUnseenPersonalMessage - ): ChatListEntrySummaryComponents.Component( - tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), - actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) - ), - ChatListEntryMessageTagSummaryKey( - tag: .unseenReaction, - actionType: PendingMessageActionType.readReaction - ): ChatListEntrySummaryComponents.Component( - tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), - actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) - ) - ] - ) - ) + let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: component.peerId) + let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: component.peerId) - let readStateKey: PostboxViewKey = .combinedReadState(peerId: component.peerId, handleThreads: false) - - let threadListSignal: Signal = component.context.account.postbox.combinedView(keys: [viewKey, readStateKey]) + let accountPeerId = component.context.account.peerId + let threadListSignal: Signal = component.context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey]) |> map { views -> EngineChatList in - guard let view = views.views[viewKey] as? MessageHistoryThreadIndexView else { - preconditionFailure() - } - guard let readStateView = views.views[readStateKey] as? CombinedReadStateView else { + guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { preconditionFailure() } - var maxReadId: Int32 = 0 - if let state = readStateView.state?.states.first(where: { $0.0 == Namespaces.Message.Cloud }) { - if case let .idBased(maxIncomingReadId, _, _, _, _) = state.1 { - maxReadId = maxIncomingReadId - } - } - - var items: [EngineChatList.Item] = [] - for item in view.items { - guard let peer = view.peer else { - continue - } - guard let data = item.info.get(MessageHistoryThreadData.self) else { - continue - } - - let defaultPeerNotificationSettings: TelegramPeerNotificationSettings = (view.peerNotificationSettings as? TelegramPeerNotificationSettings) ?? .defaultSettings - - var hasUnseenMentions = false - - var isMuted = false - switch data.notificationSettings.muteState { - case .muted: - isMuted = true - case .unmuted: - isMuted = false - case .default: - if case .default = data.notificationSettings.muteState { - if case .muted = defaultPeerNotificationSettings.muteState { - isMuted = true - } - } - } - - if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey( - tag: .unseenPersonalMessage, - actionType: PendingMessageActionType.consumeUnseenPersonalMessage - )] { - hasUnseenMentions = (info.tagSummaryCount ?? 0) > (info.actionsSummaryCount ?? 0) - } - - var hasUnseenReactions = false - if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey( - tag: .unseenReaction, - actionType: PendingMessageActionType.readReaction - )] { - hasUnseenReactions = (info.tagSummaryCount ?? 0) != 0// > (info.actionsSummaryCount ?? 0) - } - - let pinnedIndex: EngineChatList.Item.PinnedIndex - if let index = item.pinnedIndex { - pinnedIndex = .index(index) - } else { - pinnedIndex = .none - } - - var topicMaxIncomingReadId = data.maxIncomingReadId - if data.maxIncomingReadId == 0 && maxReadId != 0 && Int64(maxReadId) <= item.id { - topicMaxIncomingReadId = max(topicMaxIncomingReadId, maxReadId) - } - - let readCounters = EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: topicMaxIncomingReadId, maxOutgoingReadId: data.maxOutgoingReadId, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))]), isMuted: false) - - var draft: EngineChatList.Draft? - if let embeddedState = item.embeddedInterfaceState, let _ = embeddedState.overrideChatTimestamp { + var draft: EngineChatList.Draft? + if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView { + if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp { if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { if let text = opaqueState.synchronizeableInputState?.text { draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? []) } } } + } + + var items: [EngineChatList.Item] = [] + for item in view.items { + guard let sourcePeer = item.peer else { + continue + } + + let sourceId = PeerId(item.id) + + var messages: [EngineMessage] = [] + if let topMessage = item.topMessage { + messages.append(EngineMessage(topMessage)) + } + + let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) items.append(EngineChatList.Item( - id: .forum(item.id), - index: .forum(pinnedIndex: pinnedIndex, timestamp: item.index.timestamp, threadId: item.id, namespace: item.index.id.namespace, id: item.index.id.id), - messages: item.topMessage.flatMap { [EngineMessage($0)] } ?? [], - readCounters: readCounters, - isMuted: isMuted, - draft: draft, - threadData: data, - renderedPeer: EngineRenderedPeer(peer: EnginePeer(peer)), + id: .chatList(sourceId), + index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), + messages: messages, + readCounters: nil, + isMuted: false, + draft: sourceId == accountPeerId ? draft : nil, + threadData: nil, + renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), presence: nil, - hasUnseenMentions: hasUnseenMentions, - hasUnseenReactions: hasUnseenReactions, + hasUnseenMentions: false, + hasUnseenReactions: false, forumTopicData: nil, topForumTopicItems: [], hasFailed: false, @@ -709,6 +669,7 @@ public final class ChatSideTopicsPanel: Component { hasLater: false, isLoading: view.isLoading ) + return list } @@ -884,9 +845,7 @@ public final class ChatSideTopicsPanel: Component { guard let self, let component = self.component else { return } - guard case let .forum(topicId) = chatListItem.id else { - return - } + let topicId = chatListItem.renderedPeer.peerId.toInt64() component.updateTopicId(topicId) }, contextGesture: { gesture, sourceNode in }) @@ -895,7 +854,7 @@ public final class ChatSideTopicsPanel: Component { } var isSelected = false - if case let .forum(topicId) = item.item.id, component.topicId == topicId { + if component.topicId == item.item.renderedPeer.peerId.toInt64() { isSelected = true } let itemSize = itemView.update(context: component.context, item: item, isSelected: isSelected, theme: component.theme, width: panelWidth, transition: .immediate) diff --git a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/PeerListItemComponent.swift index 79c65a9ef1..5ed56a8cca 100644 --- a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/PeerListItemComponent.swift @@ -205,7 +205,7 @@ final class PeerListItemComponent: Component { } if let peer = component.peer { let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index 55c3294c60..80bcc2c849 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -636,7 +636,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { state = .info(string, .generic) } } else if let channel = peer as? TelegramChannel { - if channel.flags.contains(.isForum), customTitle != nil { + if channel.isForumOrMonoForum, customTitle != nil { let string = NSAttributedString(string: EnginePeer(peer).displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor) state = .info(string, .generic) } else if let cachedChannelData = peerView.cachedData as? CachedChannelData, let memberCount = onlineMemberCount.total ?? cachedChannelData.participantsSummary.memberCount { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index 9602ad62c4..a36d320ea3 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -210,7 +210,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS channelMessageId: nil, isChannelPost: false, isForumPost: false, - isMonoforum: false, + isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, @@ -379,7 +379,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS if case let .peer(peerData) = item.content { let threadId = peerData.peer.peerId.toInt64() let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - peerId: self.context.account.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: self.context.account.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil) chatController.canReadHistory.set(false) let source: ContextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: parentController.navigationController as? NavigationController)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift index bd23911577..b00988f609 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift @@ -149,7 +149,7 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro self.coveringView = UIView() - self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .replyThread(message: ChatReplyThreadMessage(peerId: context.account.peerId, threadId: peerId.toInt64(), channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)), subject: nil, botStart: nil, mode: .standard(.embedded(invertDirection: true)), params: nil) + self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .replyThread(message: ChatReplyThreadMessage(peerId: context.account.peerId, threadId: peerId.toInt64(), channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)), subject: nil, botStart: nil, mode: .standard(.embedded(invertDirection: true)), params: nil) self.chatController.navigation_setNavigationController(navigationController()) super.init() diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift index 7adcb703a5..2d0d30fdb7 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift @@ -158,7 +158,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { } var isForum = false - if let peer, let channel = peer as? TelegramChannel, channel.isForum { + if let peer, let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { isForum = true } @@ -325,7 +325,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { var isForum = false let avatarCornerRadius: CGFloat - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { avatarCornerRadius = floor(avatarSize * 0.25) isForum = true } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index ab8742bd26..6d9d74eea6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -2317,7 +2317,7 @@ func peerInfoCanEdit(peer: Peer?, chatLocation: ChatLocation, threadData: Messag } return true } else if let peer = peer as? TelegramChannel { - if peer.flags.contains(.isForum), let threadData = threadData { + if peer.isForumOrMonoForum, let threadData = threadData { if peer.flags.contains(.isCreator) { return true } else if threadData.isOwnedByMe { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift index 99e40840d8..245df71b38 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift @@ -90,7 +90,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode { var isForum = false let avatarCornerRadius: CGFloat - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { isForum = true avatarCornerRadius = floor(avatarSize * 0.25) } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift index b585b19b98..91d9455b87 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarOverlayNode.swift @@ -71,7 +71,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode { let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear) let clipStyle: AvatarNodeClipStyle - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index af54baca78..755825d160 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -573,7 +573,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } var isForum = false - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { isForum = true } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index df451f4693..b6638b9372 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -2166,7 +2166,7 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt let ItemBanned = 11 let ItemRecentActions = 12 let ItemAffiliatePrograms = 13 - let ItemPostSuggestionsSettings = 14 + //let ItemPostSuggestionsSettings = 14 let ItemPeerAutoTranslate = 15 let isCreator = channel.flags.contains(.isCreator) @@ -2216,9 +2216,9 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt })) //TODO:localize - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: { + /*items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: { interaction.editingOpenPostSuggestionsSetup() - })) + }))*/ } if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { @@ -2543,7 +2543,7 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt })) } - if (isCreator || (channel.adminRights != nil && channel.hasPermission(.banMembers))) && cachedData.peerGeoLocation == nil, !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId, !channel.flags.contains(.isForum) { + if (isCreator || (channel.adminRights != nil && channel.hasPermission(.banMembers))) && cachedData.peerGeoLocation == nil, !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId, !channel.isForumOrMonoForum { items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { interaction.editingOpenPreHistorySetup() })) @@ -3285,7 +3285,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, _ in c?.dismiss(completion: { if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController { - if let channel = currentPeer as? TelegramChannel, channel.flags.contains(.isForum), let threadId = message.threadId { + if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone() } else { let targetLocation: NavigateToChatControllerParams.Location @@ -3447,7 +3447,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, f in c?.dismiss(completion: { if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController { - if let channel = currentPeer as? TelegramChannel, channel.flags.contains(.isForum), let threadId = message.threadId { + if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone() } else { let targetLocation: NavigateToChatControllerParams.Location @@ -5148,7 +5148,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - if let channel = data.peer as? TelegramChannel, channel.flags.contains(.isForum), self.chatLocation.threadId == nil { + if let channel = data.peer as? TelegramChannel, channel.isForumOrMonoForum, self.chatLocation.threadId == nil { if self.forumTopicNotificationExceptionsDisposable == nil { self.forumTopicNotificationExceptionsDisposable = (self.context.engine.peers.forumChannelTopicNotificationExceptions(id: channel.id) |> deliverOnMainQueue).startStrict(next: { [weak self] list in @@ -5936,7 +5936,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else { displayCustomNotificationSettings = true } - if self.data?.threadData == nil, let channel = self.data?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if self.data?.threadData == nil, let channel = self.data?.peer as? TelegramChannel, channel.isForumOrMonoForum { displayCustomNotificationSettings = true } @@ -7089,7 +7089,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private func openChatForReporting(title: String, option: Data, message: String?) { if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { //let _ = self.context.engine.peers.reportPeer(peerId: peer.id, reason: reason, message: "").startStandalone() //self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .emoji(name: "PoliceCar", text: self.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current) } else { @@ -9868,7 +9868,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } var isForum = false - if let peer = strongSelf.data?.peer as? TelegramChannel, peer.flags.contains(.isForum) { + if let peer = strongSelf.data?.peer as? TelegramChannel, peer.isForumOrMonoForum { isForum = true } @@ -11032,7 +11032,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro channelMessageId: nil, isChannelPost: false, isForumPost: false, - isMonoforum: false, + isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, @@ -11062,7 +11062,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro channelMessageId: nil, isChannelPost: false, isForumPost: false, - isMonoforum: false, + isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, @@ -13376,7 +13376,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc let navigateChatLocation: NavigateToChatControllerParams.Location if let threadId = item.threadId { navigateChatLocation = .replyThread(ChatReplyThreadMessage( - peerId: item.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: item.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )) } else { navigateChatLocation = .peer(itemPeer) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift index 9bdfe72622..abc488d7df 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift @@ -30,7 +30,7 @@ extension PeerInfoScreenImpl { let peerId = self.peerId var isForum = false - if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) { + if let peer = peer as? TelegramChannel, peer.isForumOrMonoForum { isForum = true } @@ -135,7 +135,7 @@ extension PeerInfoScreenImpl { peerType = .group } else if case let .channel(channel) = peer { if case .group = channel.info { - peerType = channel.flags.contains(.isForum) ? .forum : .group + peerType = channel.isForumOrMonoForum ? .forum : .group } else { peerType = .channel } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 3bb37cc827..7634568075 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -276,7 +276,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.peerSelectionNode.requestOpenPeer = { [weak self] peer, threadId in if let strongSelf = self, let peerSelected = strongSelf.peerSelected { - if case let .channel(peer) = peer, peer.flags.contains(.isForum), threadId == nil, strongSelf.selectForumThreads { + if case let .channel(peer) = peer, peer.isForumOrMonoForum, threadId == nil, strongSelf.selectForumThreads { let controller = PeerSelectionControllerImpl( PeerSelectionControllerParams( context: strongSelf.context, @@ -316,7 +316,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon strongSelf.openMessageFromSearchDisposable.set((_internal_storedMessageFromSearchPeer(postbox: strongSelf.context.account.postbox, peer: peer._asPeer()) |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected { - if case let .channel(peer) = peer, peer.flags.contains(.isForum), threadId == nil, strongSelf.selectForumThreads { + if case let .channel(peer) = peer, peer.isForumOrMonoForum, threadId == nil, strongSelf.selectForumThreads { let controller = PeerSelectionControllerImpl( PeerSelectionControllerParams( context: strongSelf.context, diff --git a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift index bf0e0b0cd1..dd65d8411a 100644 --- a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift @@ -238,7 +238,7 @@ final class PeerListItemComponent: Component { } if let peer = component.peer { let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift index 4747fb80f2..770ea1d610 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift @@ -271,7 +271,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { let clipStyle: AvatarNodeClipStyle switch item.peer { - case let .channel(channel) where channel.isForum: + case let .channel(channel) where channel.isForumOrMonoForum: clipStyle = .roundedRect default: clipStyle = .round diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StoragePeerListPanelComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StoragePeerListPanelComponent.swift index 0313c71a8d..620045ae87 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StoragePeerListPanelComponent.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StoragePeerListPanelComponent.swift @@ -283,7 +283,7 @@ private final class PeerListItemComponent: Component { } if let peer = component.peer { let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 39be124675..6f62e684de 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2585,8 +2585,8 @@ final class StorageUsageScreenComponent: Component { } var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId = message.threadId { + chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( @@ -2688,8 +2688,8 @@ final class StorageUsageScreenComponent: Component { } var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) - if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId = message.threadId { + chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index 8408cee489..2a94368553 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -913,7 +913,7 @@ public final class PeerListItemComponent: Component { if let peer = component.peer { let clipStyle: AvatarNodeClipStyle - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 2979941dea..fdc183766b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -2808,7 +2808,7 @@ final class StoryItemSetContainerSendMessage { let context = component.context switch navigation { case let .chat(_, subject, peekData): - if case let .channel(channel) = peerId, channel.flags.contains(.isForum) { + if case let .channel(channel) = peerId, channel.isForumOrMonoForum { controller.dismissWithoutTransitionOut() context.sharedContext.navigateToForumChannel(context: context, peerId: peerId.id, navigationController: navigationController) } else { diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 7b72a6c677..841b38138f 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -325,7 +325,7 @@ final class AuthorizedApplicationContext { let chatLocation: NavigateToChatControllerParams.Location if let _ = threadData, let threadId = firstMessage.threadId { chatLocation = .replyThread(ChatReplyThreadMessage( - peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false ).normalized) } else { guard let peer = firstMessage.peers[firstMessage.id.peerId] else { @@ -941,7 +941,7 @@ final class AuthorizedApplicationContext { let chatLocation: NavigateToChatControllerParams.Location if let threadId = threadId { chatLocation = .replyThread(ChatReplyThreadMessage( - peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )) } else { chatLocation = .peer(peer) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 010ccc7f6e..d1e97ab6bb 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -127,6 +127,2042 @@ import PostSuggestionsSettingsScreen import ChatSendStarsScreen extension ChatControllerImpl { + func reloadChatLocation() { + let context = self.context + let chatLocation = self.chatLocation + let chatLocationPeerId: PeerId? = self.chatLocation.peerId + let mode = self.mode + let subject = self.subject + let peerId = chatLocationPeerId + + switch chatLocation { + case .peer: + self.chatLocationInfoData = .peer(Promise()) + case let .replyThread(replyThreadMessage): + let promise = Promise() + if let effectiveMessageId = replyThreadMessage.effectiveMessageId { + promise.set(context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: effectiveMessageId)) + |> map { message -> Message? in + guard let message = message else { + return nil + } + return message._asMessage() + }) + } else { + promise.set(.single(nil)) + } + self.chatLocationInfoData = .replyThread(promise) + case .customChatContents: + self.chatLocationInfoData = .customChatContents + } + + let managingBot: Signal + if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser { + managingBot = self.context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.ChatManagingBot(id: peerId) + ) + |> mapToSignal { result -> Signal in + guard let result else { + return .single(nil) + } + return context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.Peer(id: result.id) + ) + |> map { botPeer -> ChatManagingBot? in + guard let botPeer else { + return nil + } + + return ChatManagingBot(bot: botPeer, isPaused: result.isPaused, canReply: result.canReply, settingsUrl: result.manageUrl) + } + } + |> distinctUntilChanged + } else { + managingBot = .single(nil) + } + + if case let .peer(peerView) = self.chatLocationInfoData, let peerId = peerId { + peerView.set(context.account.viewTracker.peerView(peerId)) + var onlineMemberCount: Signal<(total: Int32?, recent: Int32?), NoError> = .single((nil, nil)) + var hasScheduledMessages: Signal = .single(false) + + if peerId.namespace == Namespaces.Peer.CloudChannel { + let recentOnlineSignal: Signal<(total: Int32?, recent: Int32?), NoError> = peerView.get() + |> map { view -> Bool? in + if let cachedData = view.cachedData as? CachedChannelData, let peer = peerViewMainPeer(view) as? TelegramChannel { + if case .broadcast = peer.info { + return nil + } else if let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 { + return true + } else { + return false + } + } else { + return false + } + } + |> distinctUntilChanged + |> mapToSignal { isLarge -> Signal<(total: Int32?, recent: Int32?), NoError> in + if let isLarge = isLarge { + if isLarge { + return context.peerChannelMemberCategoriesContextsManager.recentOnline(account: context.account, accountPeerId: context.account.peerId, peerId: peerId) + |> map { value -> (total: Int32?, recent: Int32?) in + return (nil, value) + } + } else { + return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + |> map { value -> (total: Int32?, recent: Int32?) in + return (value.total, value.recent) + } + } + } else { + return .single((nil, nil)) + } + } + onlineMemberCount = recentOnlineSignal + + self.reportIrrelvantGeoNoticePromise.set(context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.irrelevantPeerGeoReportKey(peerId: peerId))) + |> map { entry -> Bool? in + if let _ = entry?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + }) + } else { + self.reportIrrelvantGeoNoticePromise.set(.single(nil)) + } + + var isScheduledOrPinnedMessages = false + switch self.subject { + case .scheduledMessages, .pinnedMessages, .messageOptions: + isScheduledOrPinnedMessages = true + default: + break + } + + if chatLocation.peerId != nil, !isScheduledOrPinnedMessages, peerId.namespace != Namespaces.Peer.SecretChat { + let chatLocationContextHolder = self.chatLocationContextHolder + hasScheduledMessages = peerView.get() + |> take(1) + |> mapToSignal { view -> Signal in + if let peer = peerViewMainPeer(view) as? TelegramChannel, !peer.hasPermission(.sendSomething) { + return .single(false) + } else { + return context.account.viewTracker.scheduledMessagesViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder)) + |> map { view, _, _ in + return !view.entries.isEmpty + } + } + } + } + + var displayedCountSignal: Signal = .single(nil) + var subtitleTextSignal: Signal = .single(nil) + if case .pinnedMessages = subject { + displayedCountSignal = self.topPinnedMessageSignal(latest: true) + |> map { message -> Int? in + return message?.totalCount + } + |> distinctUntilChanged + } else if case let .messageOptions(peerIds, messageIds, info) = subject { + displayedCountSignal = self.presentationInterfaceStatePromise.get() + |> map { state -> Int? in + if let selectionState = state.interfaceState.selectionState { + return selectionState.selectedIds.count + } else { + return messageIds.count + } + } + |> distinctUntilChanged + + let peers = self.context.account.postbox.multiplePeersView(peerIds) + |> take(1) + + let presentationData = self.presentationData + + switch info { + case let .forward(forward): + subtitleTextSignal = combineLatest(peers, forward.options, displayedCountSignal) + |> map { peersView, options, count in + let peers = peersView.peers.values + if !peers.isEmpty { + if peers.count == 1, let peer = peers.first { + if let peer = peer as? TelegramUser { + let displayName = EnginePeer(peer).compactDisplayTitle + if count == 1 { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_UserMessageForwardHidden(displayName).string + } else { + return presentationData.strings.Conversation_ForwardOptions_UserMessageForwardVisible(displayName).string + } + } else { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_UserMessagesForwardHidden(displayName).string + } else { + return presentationData.strings.Conversation_ForwardOptions_UserMessagesForwardVisible(displayName).string + } + } + } else if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + if count == 1 { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_ChannelMessageForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_ChannelMessageForwardVisible + } + } else { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_ChannelMessagesForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_ChannelMessagesForwardVisible + } + } + } else { + if count == 1 { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_GroupMessageForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_GroupMessageForwardVisible + } + } else { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_GroupMessagesForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_GroupMessagesForwardVisible + } + } + } + } else { + if count == 1 { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_RecipientsMessageForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_RecipientsMessageForwardVisible + } + } else { + if options.hideNames { + return presentationData.strings.Conversation_ForwardOptions_RecipientsMessagesForwardHidden + } else { + return presentationData.strings.Conversation_ForwardOptions_RecipientsMessagesForwardVisible + } + } + } + } else { + return nil + } + } + case let .reply(reply): + subtitleTextSignal = reply.selectionState.get() + |> map { selectionState -> String? in + if !selectionState.canQuote { + return nil + } + return presentationData.strings.Chat_SubtitleQuoteSelectionTip + } + case let .link(link): + subtitleTextSignal = link.options + |> map { options -> String? in + if options.hasAlternativeLinks { + return presentationData.strings.Chat_SubtitleLinkListTip + } else { + return nil + } + } + |> distinctUntilChanged + } + } + + let hasPeerInfo: Signal + if peerId == context.account.peerId { + hasPeerInfo = .single(true) + |> then( + hasAvailablePeerInfoMediaPanes(context: context, peerId: peerId) + ) + } else { + hasPeerInfo = .single(true) + } + + enum MessageOptionsTitleInfo { + case reply(hasQuote: Bool) + } + let messageOptionsTitleInfo: Signal + if case let .messageOptions(_, _, info) = self.subject { + switch info { + case .forward, .link: + messageOptionsTitleInfo = .single(nil) + case let .reply(reply): + messageOptionsTitleInfo = reply.selectionState.get() + |> map { selectionState -> Bool in + return selectionState.quote != nil + } + |> distinctUntilChanged + |> map { hasQuote -> MessageOptionsTitleInfo in + return .reply(hasQuote: hasQuote) + } + } + } else { + messageOptionsTitleInfo = .single(nil) + } + + self.titleDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount, displayedCountSignal, subtitleTextSignal, self.presentationInterfaceStatePromise.get(), hasPeerInfo, messageOptionsTitleInfo) + |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, presentationInterfaceState, hasPeerInfo, messageOptionsTitleInfo in + if let strongSelf = self { + var isScheduledMessages = false + if case .scheduledMessages = presentationInterfaceState.subject { + isScheduledMessages = true + } + + if case let .messageOptions(_, _, info) = presentationInterfaceState.subject { + if case .reply = info { + let titleContent: ChatTitleContent + if case let .reply(hasQuote) = messageOptionsTitleInfo, hasQuote { + titleContent = .custom(presentationInterfaceState.strings.Chat_TitleQuoteSelection, subtitleText, false) + } else { + titleContent = .custom(presentationInterfaceState.strings.Chat_TitleReply, subtitleText, false) + } + if strongSelf.chatTitleView?.titleContent != titleContent { + if strongSelf.chatTitleView?.titleContent != nil { + strongSelf.chatTitleView?.animateLayoutTransition() + } + strongSelf.chatTitleView?.titleContent = titleContent + } + } else if case .link = info { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitleLinkOptions, subtitleText, false) + } else if displayedCount == 1 { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Conversation_ForwardOptions_ForwardTitleSingle, subtitleText, false) + } else { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Conversation_ForwardOptions_ForwardTitle(Int32(displayedCount ?? 1)), subtitleText, false) + } + } else if let selectionState = presentationInterfaceState.interfaceState.selectionState { + if selectionState.selectedIds.count > 0 { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count)), nil, false) + } else { + if let (title, _, _) = presentationInterfaceState.reportReason { + strongSelf.chatTitleView?.titleContent = .custom(title, presentationInterfaceState.strings.Conversation_SelectMessages, false) + } else { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Conversation_SelectMessages, nil, false) + } + } + } else if let peer = peerViewMainPeer(peerView) { + if case .pinnedMessages = presentationInterfaceState.subject { + strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false) + } else { + strongSelf.chatTitleView?.titleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) + let imageOverride: AvatarNodeImageOverride? + if strongSelf.context.account.peerId == peer.id { + imageOverride = .savedMessagesIcon + } else if peer.id.isReplies { + imageOverride = .repliesIcon + } else if peer.id.isAnonymousSavedMessages { + imageOverride = .anonymousSavedMessagesIcon(isColored: true) + } else if peer.isDeleted { + imageOverride = .deletedIcon + } else { + imageOverride = nil + } + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: EnginePeer(peer), overrideImage: imageOverride) + if case .standard(.previewing) = strongSelf.mode { + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = false + } else { + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil + } + strongSelf.chatInfoNavigationButton?.buttonItem.accessibilityLabel = presentationInterfaceState.strings.Conversation_ContextMenuOpenProfile + + strongSelf.storyStats = peerView.storyStats + if let avatarNode = strongSelf.avatarNode { + avatarNode.avatarNode.setStoryStats(storyStats: peerView.storyStats.flatMap { storyStats -> AvatarNode.StoryStats? in + if storyStats.totalCount == 0 { + return nil + } + if storyStats.unseenCount == 0 { + return nil + } + return AvatarNode.StoryStats( + totalCount: storyStats.totalCount, + unseenCount: storyStats.unseenCount, + hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends + ) + }, presentationParams: AvatarNode.StoryPresentationParams( + colors: AvatarNode.Colors(theme: strongSelf.presentationData.theme), + lineWidth: 1.5, + inactiveLineWidth: 1.5 + ), transition: .immediate) + } + } + } + } + })) + + let threadInfo: Signal + if let threadId = self.chatLocation.threadId { + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId) + threadInfo = context.account.postbox.combinedView(keys: [viewKey]) + |> map { views -> EngineMessageHistoryThread.Info? in + guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { + return nil + } + guard let data = view.info?.data.get(MessageHistoryThreadData.self) else { + return nil + } + return data.info + } + |> distinctUntilChanged + } else { + threadInfo = .single(nil) + } + + let hasSearchTags: Signal + if let peerId = self.chatLocation.peerId, peerId == context.account.peerId { + hasSearchTags = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId) + ) + |> map { tags -> Bool in + return !tags.isEmpty + } + |> distinctUntilChanged + } else { + hasSearchTags = .single(false) + } + + let hasSavedChats: Signal + if case .peer(context.account.peerId) = self.chatLocation { + hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved() + } else { + hasSavedChats = .single(false) + } + + let isPremiumRequiredForMessaging: Signal + if let peerId = self.chatLocation.peerId { + isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId) + |> distinctUntilChanged + } else { + isPremiumRequiredForMessaging = .single(false) + } + + let adMessage: Signal + if let adMessagesContext = self.chatDisplayNode.historyNode.adMessagesContext { + adMessage = adMessagesContext.state |> map { $0.messages.first } + } else { + adMessage = .single(nil) + } + + let displayedPeerVerification: Signal + if let peerId = self.chatLocation.peerId { + displayedPeerVerification = ApplicationSpecificNotice.displayedPeerVerification(accountManager: context.sharedContext.accountManager, peerId: peerId) + |> take(1) + } else { + displayedPeerVerification = .single(false) + } + + let globalPrivacySettings = context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalPrivacy()) + + self.peerDisposable.set(combineLatest( + queue: Queue.mainQueue(), + peerView.get(), + context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()), + onlineMemberCount, + hasScheduledMessages, + self.reportIrrelvantGeoNoticePromise.get(), + displayedCountSignal, + threadInfo, + hasSearchTags, + hasSavedChats, + isPremiumRequiredForMessaging, + managingBot, + adMessage, + displayedPeerVerification, + globalPrivacySettings + ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, adMessage, displayedPeerVerification, globalPrivacySettings in + if let strongSelf = self { + if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice && strongSelf.hasScheduledMessages == hasScheduledMessages && strongSelf.threadInfo == threadInfo && strongSelf.presentationInterfaceState.hasSearchTags == hasSearchTags && strongSelf.presentationInterfaceState.hasSavedChats == hasSavedChats && strongSelf.presentationInterfaceState.isPremiumRequiredForMessaging == isPremiumRequiredForMessaging && managingBot == strongSelf.presentationInterfaceState.contactStatus?.managingBot && adMessage?.id == strongSelf.presentationInterfaceState.adMessage?.id { + return + } + + strongSelf.reportIrrelvantGeoNotice = peerReportNotice + strongSelf.hasScheduledMessages = hasScheduledMessages + + var upgradedToPeerId: PeerId? + var movedToForumTopics = false + if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.migrationReference == nil, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, let migrationReference = updatedGroup.migrationReference { + upgradedToPeerId = migrationReference.peerId + } + if let previous = strongSelf.peerView, let channel = previous.peers[previous.peerId] as? TelegramChannel, !channel.isForumOrMonoForum, let updatedChannel = peerView.peers[peerView.peerId] as? TelegramChannel, updatedChannel.isForumOrMonoForum { + movedToForumTopics = true + } + + var shouldDismiss = false + if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.membership != .Removed, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, updatedGroup.membership == .Removed { + shouldDismiss = true + } else if let previous = strongSelf.peerView, let channel = previous.peers[previous.peerId] as? TelegramChannel, channel.participationStatus != .kicked, let updatedChannel = peerView.peers[peerView.peerId] as? TelegramChannel, updatedChannel.participationStatus == .kicked { + shouldDismiss = true + } else if let previous = strongSelf.peerView, let secretChat = previous.peers[previous.peerId] as? TelegramSecretChat, case .active = secretChat.embeddedState, let updatedSecretChat = peerView.peers[peerView.peerId] as? TelegramSecretChat, case .terminated = updatedSecretChat.embeddedState { + shouldDismiss = true + } + + var wasGroupChannel: Bool? + if let previousPeerView = strongSelf.peerView, let info = (previousPeerView.peers[previousPeerView.peerId] as? TelegramChannel)?.info { + if case .group = info { + wasGroupChannel = true + } else { + wasGroupChannel = false + } + } + var isGroupChannel: Bool? + if let info = (peerView.peers[peerView.peerId] as? TelegramChannel)?.info { + if case .group = info { + isGroupChannel = true + } else { + isGroupChannel = false + } + } + let firstTime = strongSelf.peerView == nil + strongSelf.peerView = peerView + strongSelf.threadInfo = threadInfo + if wasGroupChannel != isGroupChannel { + if let isGroupChannel = isGroupChannel, isGroupChannel { + let (recentDisposable, _) = strongSelf.context.peerChannelMemberCategoriesContextsManager.recent(engine: strongSelf.context.engine, postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { _ in }) + let (adminsDisposable, _) = strongSelf.context.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.context.engine, postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { _ in }) + let disposable = DisposableSet() + disposable.add(recentDisposable) + disposable.add(adminsDisposable) + strongSelf.chatAdditionalDataDisposable.set(disposable) + } else { + strongSelf.chatAdditionalDataDisposable.set(nil) + } + } + if strongSelf.isNodeLoaded { + strongSelf.chatDisplayNode.overlayTitle = strongSelf.overlayTitle + } + var peerIsMuted = false + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + peerIsMuted = true + } else if case .default = notificationSettings.muteState { + if let peer = peerView.peers[peerView.peerId] { + if peer is TelegramUser { + peerIsMuted = !globalNotificationSettings.privateChats.enabled + } else if peer is TelegramGroup { + peerIsMuted = !globalNotificationSettings.groupChats.enabled + } else if let channel = peer as? TelegramChannel { + switch channel.info { + case .group: + peerIsMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + peerIsMuted = !globalNotificationSettings.channels.enabled + } + } + } + } + } + var starGiftsAvailable = false + var peerDiscussionId: PeerId? + var peerGeoLocation: PeerGeoLocation? + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData { + if case .broadcast = peer.info { + starGiftsAvailable = cachedData.flags.contains(.starGiftsAvailable) + } else { + peerGeoLocation = cachedData.peerGeoLocation + } + if case let .known(value) = cachedData.linkedDiscussionPeerId { + peerDiscussionId = value + } + } + var renderedPeer: RenderedPeer? + var contactStatus: ChatContactStatus? + var businessIntro: TelegramBusinessIntro? + var sendPaidMessageStars: StarsAmount? + var alwaysShowGiftButton = false + var disallowedGifts: TelegramDisallowedGifts? + if let peer = peerView.peers[peerView.peerId] { + if let cachedData = peerView.cachedData as? CachedUserData { + contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot) + if case let .known(value) = cachedData.businessIntro { + businessIntro = value + } + if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + } else { + sendPaidMessageStars = cachedData.sendPaidMessageStars + if cachedData.disallowedGifts != .All { + alwaysShowGiftButton = globalPrivacySettings.displayGiftButton || cachedData.flags.contains(.displayGiftButton) + } + disallowedGifts = cachedData.disallowedGifts + } + } else if let cachedData = peerView.cachedData as? CachedGroupData { + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + } else if let cachedData = peerView.cachedData as? CachedChannelData { + var canReportIrrelevantLocation = true + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member { + canReportIrrelevantLocation = false + } + if let peerReportNotice = peerReportNotice, peerReportNotice { + canReportIrrelevantLocation = false + } + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + + if let channel = peerView.peers[peerView.peerId] as? TelegramChannel { + if channel.flags.contains(.isCreator) || channel.adminRights != nil { + } else { + sendPaidMessageStars = channel.sendPaidMessageStars + } + } + } + + var peers = SimpleDictionary() + peers[peer.id] = peer + if let associatedPeerId = peer.associatedPeerId, let associatedPeer = peerView.peers[associatedPeerId] { + peers[associatedPeer.id] = associatedPeer + } + renderedPeer = RenderedPeer(peerId: peer.id, peers: peers, associatedMedia: peerView.media) + } + + var isNotAccessible: Bool = false + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + isNotAccessible = cachedChannelData.isNotAccessible + } + + if firstTime && isNotAccessible { + strongSelf.context.account.viewTracker.forceUpdateCachedPeerData(peerId: peerView.peerId) + } + + var hasBots: Bool = false + var hasBotCommands: Bool = false + var botMenuButton: BotMenuButton = .commands + var currentSendAsPeerId: PeerId? + var autoremoveTimeout: Int32? + var copyProtectionEnabled: Bool = false + var hasBirthdayToday = false + var peerVerification: PeerVerification? + if let peer = peerView.peers[peerView.peerId] { + if !displayedPeerVerification { + if let cachedUserData = peerView.cachedData as? CachedUserData { + peerVerification = cachedUserData.verification + } else if let cachedChannelData = peerView.cachedData as? CachedChannelData { + peerVerification = cachedChannelData.verification + } + } + copyProtectionEnabled = peer.isCopyProtectionEnabled + if let cachedGroupData = peerView.cachedData as? CachedGroupData { + if !cachedGroupData.botInfos.isEmpty { + hasBots = true + } + let botCommands = cachedGroupData.botInfos.reduce(into: [], { result, info in + result.append(contentsOf: info.botInfo.commands) + }) + if !botCommands.isEmpty { + hasBotCommands = true + } + if case let .known(value) = cachedGroupData.autoremoveTimeout { + autoremoveTimeout = value?.effectiveValue + } + } else if let cachedChannelData = peerView.cachedData as? CachedChannelData { + currentSendAsPeerId = cachedChannelData.sendAsPeerId + if let channel = peer as? TelegramChannel, case .group = channel.info { + if !cachedChannelData.botInfos.isEmpty { + hasBots = true + } + let botCommands = cachedChannelData.botInfos.reduce(into: [], { result, info in + result.append(contentsOf: info.botInfo.commands) + }) + if !botCommands.isEmpty { + hasBotCommands = true + } + } + if case let .known(value) = cachedChannelData.autoremoveTimeout { + autoremoveTimeout = value?.effectiveValue + } + } else if let cachedUserData = peerView.cachedData as? CachedUserData { + botMenuButton = cachedUserData.botInfo?.menuButton ?? .commands + if case let .known(value) = cachedUserData.autoremoveTimeout { + autoremoveTimeout = value?.effectiveValue + } + if let botInfo = cachedUserData.botInfo, !botInfo.commands.isEmpty { + hasBotCommands = true + } + if let birthday = cachedUserData.birthday { + let today = Calendar.current.dateComponents(Set([.day, .month]), from: Date()) + if today.day == Int(birthday.day) && today.month == Int(birthday.month) { + hasBirthdayToday = true + } + } + } + } + + let isArchived: Bool = peerView.groupId == Namespaces.PeerGroup.archive + + var explicitelyCanPinMessages: Bool = false + if let cachedUserData = peerView.cachedData as? CachedUserData { + explicitelyCanPinMessages = cachedUserData.canPinMessages + } else if peerView.peerId == context.account.peerId { + explicitelyCanPinMessages = true + } + + var animated = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat, let updated = renderedPeer?.peer as? TelegramSecretChat, peer.embeddedState != updated.embeddedState { + animated = true + } + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, let updated = renderedPeer?.peer as? TelegramChannel { + if peer.participationStatus != updated.participationStatus { + animated = true + } + } + + var didDisplayActionsPanel = false + if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + didDisplayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.suggestAddMembers) { + didDisplayActionsPanel = true + } + } + } + if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, contactStatus.managingBot != nil { + didDisplayActionsPanel = true + } + if strongSelf.presentationInterfaceState.search != nil && strongSelf.presentationInterfaceState.hasSearchTags { + didDisplayActionsPanel = true + } + + var displayActionsPanel = false + if let contactStatus = contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + displayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.suggestAddMembers) { + displayActionsPanel = true + } + } + } + if let contactStatus, contactStatus.managingBot != nil { + displayActionsPanel = true + } + if strongSelf.presentationInterfaceState.search != nil && hasSearchTags { + displayActionsPanel = true + } + + if displayActionsPanel != didDisplayActionsPanel { + animated = true + } + + if strongSelf.preloadHistoryPeerId != peerDiscussionId { + strongSelf.preloadHistoryPeerId = peerDiscussionId + if let peerDiscussionId = peerDiscussionId, let channel = peerView.peers[peerView.peerId] as? TelegramChannel, case .broadcast = channel.info { + let combinedDisposable = DisposableSet() + strongSelf.preloadHistoryPeerIdDisposable.set(combinedDisposable) + combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: peerDiscussionId).startStrict()) + combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerDiscussionId)) + } else { + strongSelf.preloadHistoryPeerIdDisposable.set(nil) + } + } + + var appliedBoosts: Int32? + var boostsToUnrestrict: Int32? + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + appliedBoosts = cachedChannelData.appliedBoosts + boostsToUnrestrict = cachedChannelData.boostsToUnrestrict + } + + if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = strongSelf.chatLocation.peerId { + strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone() + } + + var adMessage = adMessage + if let peer = peerView.peers[peerView.peerId] as? TelegramUser, peer.botInfo != nil { + } else { + adMessage = nil + } + + if strongSelf.presentationInterfaceState.adMessage?.id != adMessage?.id { + animated = true + } + + strongSelf.updateChatPresentationInterfaceState(animated: animated, interactive: false, { + return $0.updatedPeer { _ in + return renderedPeer + }.updatedIsNotAccessible(isNotAccessible) + .updatedContactStatus(contactStatus) + .updatedHasBots(hasBots) + .updatedHasBotCommands(hasBotCommands) + .updatedBotMenuButton(botMenuButton) + .updatedIsArchived(isArchived) + .updatedPeerIsMuted(peerIsMuted) + .updatedPeerDiscussionId(peerDiscussionId) + .updatedPeerGeoLocation(peerGeoLocation) + .updatedExplicitelyCanPinMessages(explicitelyCanPinMessages) + .updatedHasScheduledMessages(hasScheduledMessages) + .updatedAutoremoveTimeout(autoremoveTimeout) + .updatedCurrentSendAsPeerId(currentSendAsPeerId) + .updatedCopyProtectionEnabled(copyProtectionEnabled) + .updatedHasSearchTags(hasSearchTags) + .updatedIsPremiumRequiredForMessaging(isPremiumRequiredForMessaging) + .updatedSendPaidMessageStars(sendPaidMessageStars) + .updatedAlwaysShowGiftButton(alwaysShowGiftButton) + .updatedDisallowedGifts(disallowedGifts) + .updatedHasSavedChats(hasSavedChats) + .updatedAppliedBoosts(appliedBoosts) + .updatedBoostsToUnrestrict(boostsToUnrestrict) + .updatedHasBirthdayToday(hasBirthdayToday) + .updatedBusinessIntro(businessIntro) + .updatedAdMessage(adMessage) + .updatedPeerVerification(peerVerification) + .updatedStarGiftsAvailable(starGiftsAvailable) + .updatedInterfaceState { interfaceState in + var interfaceState = interfaceState + + if let channel = renderedPeer?.peer as? TelegramChannel { + if channel.hasBannedPermission(.banSendVoice) != nil && channel.hasBannedPermission(.banSendInstantVideos) != nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } else if channel.hasBannedPermission(.banSendVoice) != nil { + if channel.hasBannedPermission(.banSendInstantVideos) == nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.video) + } + } else if channel.hasBannedPermission(.banSendInstantVideos) != nil { + if channel.hasBannedPermission(.banSendVoice) == nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } + } + } else if let group = renderedPeer?.peer as? TelegramGroup { + if group.hasBannedPermission(.banSendVoice) && group.hasBannedPermission(.banSendInstantVideos) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } else if group.hasBannedPermission(.banSendVoice) { + if !group.hasBannedPermission(.banSendInstantVideos) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.video) + } + } else if group.hasBannedPermission(.banSendInstantVideos) { + if !group.hasBannedPermission(.banSendVoice) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } + } + } + + return interfaceState + } + }) + + if case .standard(.default) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { + var isRegularChat = false + if let subject = subject { + if case .message = subject { + isRegularChat = true + } + } else { + isRegularChat = true + } + if strongSelf.nextChannelToReadDisposable == nil, let peerId = strongSelf.chatLocation.peerId, let customChatNavigationStack = strongSelf.customChatNavigationStack { + if let index = customChatNavigationStack.firstIndex(of: peerId), index != customChatNavigationStack.count - 1 { + let nextPeerId = customChatNavigationStack[index + 1] + strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), + strongSelf.context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.Peer(id: nextPeerId) + ), + ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) + ) + |> then(.complete() |> delay(1.0, queue: .mainQueue())) + |> restart).startStrict(next: { nextPeer, nextChatSuggestionTip in + guard let strongSelf = self else { + return + } + + strongSelf.offerNextChannelToRead = true + strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextPeer.flatMap { nextPeer -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in + return (peer: nextPeer, threadData: nil, unreadCount: 0, location: .same) + } + strongSelf.chatDisplayNode.historyNode.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3 + + let nextPeerId = nextPeer?.id + + if strongSelf.preloadNextChatPeerId != nextPeerId { + strongSelf.preloadNextChatPeerId = nextPeerId + if let nextPeerId = nextPeerId { + let combinedDisposable = DisposableSet() + strongSelf.preloadNextChatPeerIdDisposable.set(combinedDisposable) + combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: nextPeerId).startStrict()) + combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: nextPeerId)) + } else { + strongSelf.preloadNextChatPeerIdDisposable.set(nil) + } + } + + strongSelf.updateNextChannelToReadVisibility() + }) + } + } else if isRegularChat, strongSelf.nextChannelToReadDisposable == nil { + //TODO:loc optimize + let accountPeerId = strongSelf.context.account.peerId + strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), + strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, chatListFilterId: strongSelf.currentChatListFilter, getFilterPredicate: { data in + return chatListFilterPredicate(filter: data, accountPeerId: accountPeerId) + }), + ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) + ) + |> then(.complete() |> delay(1.0, queue: .mainQueue())) + |> restart).startStrict(next: { nextPeer, nextChatSuggestionTip in + guard let strongSelf = self else { + return + } + + strongSelf.offerNextChannelToRead = true + strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextPeer.flatMap { nextPeer -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in + return (peer: nextPeer.peer, threadData: nil, unreadCount: nextPeer.unreadCount, location: nextPeer.location) + } + strongSelf.chatDisplayNode.historyNode.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3 + + let nextPeerId = nextPeer?.peer.id + + if strongSelf.preloadNextChatPeerId != nextPeerId { + strongSelf.preloadNextChatPeerId = nextPeerId + if let nextPeerId = nextPeerId { + let combinedDisposable = DisposableSet() + strongSelf.preloadNextChatPeerIdDisposable.set(combinedDisposable) + combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: nextPeerId).startStrict()) + combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: nextPeerId)) + } else { + strongSelf.preloadNextChatPeerIdDisposable.set(nil) + } + } + + strongSelf.updateNextChannelToReadVisibility() + }) + } + } + + if !strongSelf.didSetChatLocationInfoReady { + strongSelf.didSetChatLocationInfoReady = true + strongSelf._chatLocationInfoReady.set(.single(true)) + } + strongSelf.updateReminderActivity() + if let upgradedToPeerId = upgradedToPeerId { + if let navigationController = strongSelf.effectiveNavigationController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === strongSelf }) { + viewControllers[index] = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: upgradedToPeerId)) + navigationController.setViewControllers(viewControllers, animated: false) + } + } + } else if movedToForumTopics { + if let navigationController = strongSelf.effectiveNavigationController { + let chatListController = strongSelf.context.sharedContext.makeChatListController(context: strongSelf.context, location: .forum(peerId: peerView.peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) + navigationController.replaceController(strongSelf, with: chatListController, animated: true) + } + } else if shouldDismiss { + strongSelf.dismiss() + } + } + })) + + if peerId == context.account.peerId { + self.preloadSavedMessagesChatsDisposable = context.engine.messages.savedMessagesPeerListHead().start() + } + } else if case let .replyThread(messagePromise) = self.chatLocationInfoData, let peerId = peerId { + self.reportIrrelvantGeoNoticePromise.set(.single(nil)) + + let replyThreadType: ChatTitleContent.ReplyThreadType + var replyThreadId: Int64? + switch chatLocation { + case .peer: + replyThreadType = .replies + case let .replyThread(replyThreadMessage): + if replyThreadMessage.peerId == context.account.peerId { + replyThreadId = replyThreadMessage.threadId + replyThreadType = .replies + } else { + replyThreadId = replyThreadMessage.threadId + if replyThreadMessage.isChannelPost { + replyThreadType = .comments + } else { + replyThreadType = .replies + } + } + case .customChatContents: + replyThreadType = .replies + } + + let peerView = context.account.viewTracker.peerView(peerId) + + let messageAndTopic = messagePromise.get() + |> mapToSignal { message -> Signal<(message: Message?, threadData: MessageHistoryThreadData?, messageCount: Int), NoError> in + guard let replyThreadId = replyThreadId else { + return .single((message, nil, 0)) + } + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: replyThreadId) + let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Cloud, customTag: nil) + let localCountViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Local, customTag: nil) + return context.account.postbox.combinedView(keys: [viewKey, countViewKey, localCountViewKey]) + |> map { views -> (message: Message?, threadData: MessageHistoryThreadData?, messageCount: Int) in + guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { + return (message, nil, 0) + } + var messageCount = 0 + if let summaryView = views.views[countViewKey] as? MessageHistoryTagSummaryView, let count = summaryView.count { + if replyThreadId == 1 { + messageCount += Int(count) + } else { + messageCount += max(Int(count) - 1, 0) + } + } + if let summaryView = views.views[localCountViewKey] as? MessageHistoryTagSummaryView, let count = summaryView.count { + messageCount += Int(count) + } + return (message, view.info?.data.get(MessageHistoryThreadData.self), messageCount) + } + } + + let savedMessagesPeerId: PeerId? + if case let .replyThread(replyThreadMessage) = chatLocation, (replyThreadMessage.peerId == context.account.peerId || replyThreadMessage.isMonoforumPost) { + savedMessagesPeerId = PeerId(replyThreadMessage.threadId) + } else { + savedMessagesPeerId = nil + } + + let savedMessagesPeer: Signal<(peer: EnginePeer?, messageCount: Int)?, NoError> + if let savedMessagesPeerId { + let threadPeerId = savedMessagesPeerId + let basicPeerKey: PostboxViewKey = .basicPeer(threadPeerId) + let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: savedMessagesPeerId.toInt64(), namespace: Namespaces.Message.Cloud, customTag: nil) + savedMessagesPeer = context.account.postbox.combinedView(keys: [basicPeerKey, countViewKey]) + |> map { views -> (peer: EnginePeer?, messageCount: Int)? in + let peer = ((views.views[basicPeerKey] as? BasicPeerView)?.peer).flatMap(EnginePeer.init) + + var messageCount = 0 + if let summaryView = views.views[countViewKey] as? MessageHistoryTagSummaryView, let count = summaryView.count { + messageCount += Int(count) + } + + return (peer, messageCount) + } + } else { + savedMessagesPeer = .single(nil) + } + + var isScheduledOrPinnedMessages = false + switch subject { + case .scheduledMessages, .pinnedMessages, .messageOptions: + isScheduledOrPinnedMessages = true + default: + break + } + + var hasScheduledMessages: Signal = .single(false) + if chatLocation.peerId != nil, !isScheduledOrPinnedMessages, peerId.namespace != Namespaces.Peer.SecretChat { + let chatLocationContextHolder = self.chatLocationContextHolder + hasScheduledMessages = peerView + |> take(1) + |> mapToSignal { view -> Signal in + if let peer = peerViewMainPeer(view) as? TelegramChannel, !peer.hasPermission(.sendSomething) { + return .single(false) + } else { + if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId { + return context.account.viewTracker.scheduledMessagesViewForLocation(context.chatLocationInput(for: .peer(id: context.account.peerId), contextHolder: Atomic(value: nil))) + |> map { view, _, _ in + return !view.entries.isEmpty + } + |> distinctUntilChanged + } else { + return context.account.viewTracker.scheduledMessagesViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder)) + |> map { view, _, _ in + return !view.entries.isEmpty + } + |> distinctUntilChanged + } + } + } + } + + var onlineMemberCount: Signal<(total: Int32?, recent: Int32?), NoError> = .single((nil, nil)) + if peerId.namespace == Namespaces.Peer.CloudChannel { + let recentOnlineSignal: Signal<(total: Int32?, recent: Int32?), NoError> = peerView + |> map { view -> Bool? in + if let cachedData = view.cachedData as? CachedChannelData, let peer = peerViewMainPeer(view) as? TelegramChannel { + if case .broadcast = peer.info { + return nil + } else if let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 { + return true + } else { + return false + } + } else { + return false + } + } + |> distinctUntilChanged + |> mapToSignal { isLarge -> Signal<(total: Int32?, recent: Int32?), NoError> in + if let isLarge = isLarge { + if isLarge { + return context.peerChannelMemberCategoriesContextsManager.recentOnline(account: context.account, accountPeerId: context.account.peerId, peerId: peerId) + |> map { value -> (total: Int32?, recent: Int32?) in + return (nil, value) + } + } else { + return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + |> map { value -> (total: Int32?, recent: Int32?) in + return (value.total, value.recent) + } + } + } else { + return .single((nil, nil)) + } + } + onlineMemberCount = recentOnlineSignal + } + + let hasSearchTags: Signal + if let peerId = self.chatLocation.peerId, peerId == context.account.peerId { + hasSearchTags = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId) + ) + |> map { tags -> Bool in + return !tags.isEmpty + } + |> distinctUntilChanged + } else { + hasSearchTags = .single(false) + } + + let hasSavedChats: Signal + if case .peer(context.account.peerId) = self.chatLocation { + hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved() + } else { + hasSavedChats = .single(false) + } + + let isPremiumRequiredForMessaging: Signal + if let peerId = self.chatLocation.peerId { + isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId) + |> distinctUntilChanged + } else { + isPremiumRequiredForMessaging = .single(false) + } + + let globalPrivacySettings = context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalPrivacy()) + + self.titleDisposable.set(nil) + self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), + peerView, + messageAndTopic, + savedMessagesPeer, + onlineMemberCount, + hasScheduledMessages, + hasSearchTags, + hasSavedChats, + isPremiumRequiredForMessaging, + managingBot, + globalPrivacySettings + ) + |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, messageAndTopic, savedMessagesPeer, onlineMemberCount, hasScheduledMessages, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, globalPrivacySettings in + if let strongSelf = self { + strongSelf.hasScheduledMessages = hasScheduledMessages + + var renderedPeer: RenderedPeer? + var contactStatus: ChatContactStatus? + var copyProtectionEnabled = false + var businessIntro: TelegramBusinessIntro? + var sendPaidMessageStars: StarsAmount? + var alwaysShowGiftButton = false + var disallowedGifts: TelegramDisallowedGifts? + if let peer = peerView.peers[peerView.peerId] { + copyProtectionEnabled = peer.isCopyProtectionEnabled + if let cachedData = peerView.cachedData as? CachedUserData { + contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot) + if case let .known(value) = cachedData.businessIntro { + businessIntro = value + } + if cachedData.disallowedGifts != .All { + alwaysShowGiftButton = globalPrivacySettings.displayGiftButton || cachedData.flags.contains(.displayGiftButton) + } + disallowedGifts = cachedData.disallowedGifts + } else if let cachedData = peerView.cachedData as? CachedGroupData { + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + } else if let cachedData = peerView.cachedData as? CachedChannelData { + var canReportIrrelevantLocation = true + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member { + canReportIrrelevantLocation = false + } + canReportIrrelevantLocation = false + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + + if let channel = peerView.peers[peerView.peerId] as? TelegramChannel { + if channel.flags.contains(.isCreator) || channel.adminRights != nil { + } else { + sendPaidMessageStars = channel.sendPaidMessageStars + } + } + } + + var peers = SimpleDictionary() + peers[peer.id] = peer + if let associatedPeerId = peer.associatedPeerId, let associatedPeer = peerView.peers[associatedPeerId] { + peers[associatedPeer.id] = associatedPeer + } + renderedPeer = RenderedPeer(peerId: peer.id, peers: peers, associatedMedia: peerView.media) + } + + if let savedMessagesPeerId { + let mappedPeerData = ChatTitleContent.PeerData( + peerId: savedMessagesPeerId, + peer: savedMessagesPeer?.peer?._asPeer(), + isContact: true, + isSavedMessages: true, + notificationSettings: nil, + peerPresences: [:], + cachedData: nil + ) + strongSelf.chatTitleView?.titleContent = .peer(peerView: mappedPeerData, customTitle: nil, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: false, customMessageCount: savedMessagesPeer?.messageCount ?? 0, isEnabled: true) + + strongSelf.peerView = peerView + + let imageOverride: AvatarNodeImageOverride? + if strongSelf.context.account.peerId == savedMessagesPeerId { + imageOverride = .myNotesIcon + } else if let peer = savedMessagesPeer?.peer, peer.id.isReplies { + imageOverride = .repliesIcon + } else if let peer = savedMessagesPeer?.peer, peer.id.isAnonymousSavedMessages { + imageOverride = .anonymousSavedMessagesIcon(isColored: true) + } else if let peer = savedMessagesPeer?.peer, peer.isDeleted { + imageOverride = .deletedIcon + } else { + imageOverride = nil + } + + if strongSelf.isNodeLoaded { + strongSelf.chatDisplayNode.overlayTitle = strongSelf.overlayTitle + } + + let animated = false + strongSelf.updateChatPresentationInterfaceState(animated: animated, interactive: false, { + return $0.updatedPeer { _ in + return renderedPeer + }.updatedSavedMessagesTopicPeer(savedMessagesPeer?.peer) + .updatedHasSearchTags(hasSearchTags) + .updatedHasSavedChats(hasSavedChats) + .updatedHasScheduledMessages(hasScheduledMessages) + }) + + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: savedMessagesPeer?.peer, overrideImage: imageOverride) + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = false + strongSelf.chatInfoNavigationButton?.buttonItem.accessibilityLabel = strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile + } else { + let message = messageAndTopic.message + + var count = 0 + if let message = message { + for attribute in message.attributes { + if let attribute = attribute as? ReplyThreadMessageAttribute { + count = Int(attribute.count) + break + } + } + } + + var peerIsMuted = false + if let threadData = messageAndTopic.threadData { + if case let .muted(until) = threadData.notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + peerIsMuted = true + } + } else if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + peerIsMuted = true + } + } + + if let threadInfo = messageAndTopic.threadData?.info { + strongSelf.chatTitleView?.titleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true) + + let avatarContent: EmojiStatusComponent.Content + if strongSelf.chatLocation.threadId == 1 { + avatarContent = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(strongSelf.presentationData.theme)) + } else if let fileId = threadInfo.icon { + avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: strongSelf.presentationData.theme.list.itemAccentColor, loopMode: .count(1)) + } else { + avatarContent = .topic(title: String(threadInfo.title.prefix(1)), color: threadInfo.iconColor, size: CGSize(width: 32.0, height: 32.0)) + } + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setStatus(context: strongSelf.context, content: avatarContent) + } else { + strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: count) + } + + var wasGroupChannel: Bool? + if let previousPeerView = strongSelf.peerView, let info = (previousPeerView.peers[previousPeerView.peerId] as? TelegramChannel)?.info { + if case .group = info { + wasGroupChannel = true + } else { + wasGroupChannel = false + } + } + var isGroupChannel: Bool? + if let info = (peerView.peers[peerView.peerId] as? TelegramChannel)?.info { + if case .group = info { + isGroupChannel = true + } else { + isGroupChannel = false + } + } + let firstTime = strongSelf.peerView == nil + + if wasGroupChannel != isGroupChannel { + if let isGroupChannel = isGroupChannel, isGroupChannel { + let (recentDisposable, _) = strongSelf.context.peerChannelMemberCategoriesContextsManager.recent(engine: strongSelf.context.engine, postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { _ in }) + let (adminsDisposable, _) = strongSelf.context.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.context.engine, postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { _ in }) + let disposable = DisposableSet() + disposable.add(recentDisposable) + disposable.add(adminsDisposable) + strongSelf.chatAdditionalDataDisposable.set(disposable) + } else { + strongSelf.chatAdditionalDataDisposable.set(nil) + } + } + + strongSelf.peerView = peerView + strongSelf.threadInfo = messageAndTopic.threadData?.info + + if strongSelf.isNodeLoaded { + strongSelf.chatDisplayNode.overlayTitle = strongSelf.overlayTitle + } + if case .standard(.previewing) = strongSelf.mode { + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = false + } else { + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = true + } + + var peerDiscussionId: PeerId? + var peerGeoLocation: PeerGeoLocation? + var currentSendAsPeerId: PeerId? + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData { + currentSendAsPeerId = cachedData.sendAsPeerId + if case .group = peer.info { + peerGeoLocation = cachedData.peerGeoLocation + } + if case let .known(value) = cachedData.linkedDiscussionPeerId { + peerDiscussionId = value + } + } + + var isNotAccessible: Bool = false + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + isNotAccessible = cachedChannelData.isNotAccessible + } + + if firstTime && isNotAccessible { + strongSelf.context.account.viewTracker.forceUpdateCachedPeerData(peerId: peerView.peerId) + } + + var hasBots: Bool = false + if let peer = peerView.peers[peerView.peerId] { + if let cachedGroupData = peerView.cachedData as? CachedGroupData { + if !cachedGroupData.botInfos.isEmpty { + hasBots = true + } + } else if let cachedChannelData = peerView.cachedData as? CachedChannelData, let channel = peer as? TelegramChannel, case .group = channel.info { + if !cachedChannelData.botInfos.isEmpty { + hasBots = true + } + } + } + + let isArchived: Bool = peerView.groupId == Namespaces.PeerGroup.archive + + var explicitelyCanPinMessages: Bool = false + if let cachedUserData = peerView.cachedData as? CachedUserData { + explicitelyCanPinMessages = cachedUserData.canPinMessages + } else if peerView.peerId == context.account.peerId { + explicitelyCanPinMessages = true + } + + var animated = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat, let updated = renderedPeer?.peer as? TelegramSecretChat, peer.embeddedState != updated.embeddedState { + animated = true + } + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, let updated = renderedPeer?.peer as? TelegramChannel { + if peer.participationStatus != updated.participationStatus { + animated = true + } + } + + var didDisplayActionsPanel = false + if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + didDisplayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.suggestAddMembers) { + didDisplayActionsPanel = true + } + } + } + if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, contactStatus.managingBot != nil { + didDisplayActionsPanel = true + } + + var displayActionsPanel = false + if let contactStatus = contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + displayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.suggestAddMembers) { + displayActionsPanel = true + } + } + } + if let contactStatus, contactStatus.managingBot != nil { + displayActionsPanel = true + } + + if displayActionsPanel != didDisplayActionsPanel { + animated = true + } + + if strongSelf.preloadHistoryPeerId != peerDiscussionId { + strongSelf.preloadHistoryPeerId = peerDiscussionId + if let peerDiscussionId = peerDiscussionId { + strongSelf.preloadHistoryPeerIdDisposable.set(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerDiscussionId)) + } else { + strongSelf.preloadHistoryPeerIdDisposable.set(nil) + } + } + + var appliedBoosts: Int32? + var boostsToUnrestrict: Int32? + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + appliedBoosts = cachedChannelData.appliedBoosts + boostsToUnrestrict = cachedChannelData.boostsToUnrestrict + } + + if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = strongSelf.chatLocation.peerId { + strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone() + } + + strongSelf.updateChatPresentationInterfaceState(animated: animated, interactive: false, { + return $0.updatedPeer { _ in + return renderedPeer + }.updatedIsNotAccessible(isNotAccessible).updatedContactStatus(contactStatus).updatedHasBots(hasBots).updatedIsArchived(isArchived).updatedPeerIsMuted(peerIsMuted).updatedPeerDiscussionId(peerDiscussionId).updatedPeerGeoLocation(peerGeoLocation).updatedExplicitelyCanPinMessages(explicitelyCanPinMessages).updatedHasScheduledMessages(hasScheduledMessages).updatedCurrentSendAsPeerId(currentSendAsPeerId) + .updatedCopyProtectionEnabled(copyProtectionEnabled) + .updatedHasSearchTags(hasSearchTags) + .updatedIsPremiumRequiredForMessaging(isPremiumRequiredForMessaging) + .updatedHasSavedChats(hasSavedChats) + .updatedAppliedBoosts(appliedBoosts) + .updatedBoostsToUnrestrict(boostsToUnrestrict) + .updatedBusinessIntro(businessIntro) + .updatedSendPaidMessageStars(sendPaidMessageStars) + .updatedAlwaysShowGiftButton(alwaysShowGiftButton) + .updatedDisallowedGifts(disallowedGifts) + .updatedInterfaceState { interfaceState in + var interfaceState = interfaceState + + if let channel = renderedPeer?.peer as? TelegramChannel { + if channel.hasBannedPermission(.banSendVoice) != nil && channel.hasBannedPermission(.banSendInstantVideos) != nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } else if channel.hasBannedPermission(.banSendVoice) != nil { + if channel.hasBannedPermission(.banSendInstantVideos) == nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.video) + } + } else if channel.hasBannedPermission(.banSendInstantVideos) != nil { + if channel.hasBannedPermission(.banSendVoice) == nil { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } + } + } else if let group = renderedPeer?.peer as? TelegramGroup { + if group.hasBannedPermission(.banSendVoice) && group.hasBannedPermission(.banSendInstantVideos) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } else if group.hasBannedPermission(.banSendVoice) { + if !group.hasBannedPermission(.banSendInstantVideos) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.video) + } + } else if group.hasBannedPermission(.banSendInstantVideos) { + if !group.hasBannedPermission(.banSendVoice) { + interfaceState = interfaceState.withUpdatedMediaRecordingMode(.audio) + } + } + } + + return interfaceState + } + }) + + if let replyThreadId, let channel = renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, strongSelf.nextChannelToReadDisposable == nil { + strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), + strongSelf.context.engine.peers.getNextUnreadForumTopic(peerId: channel.id, topicId: Int32(clamping: replyThreadId)), + ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) + ) + |> then(.complete() |> delay(1.0, queue: .mainQueue())) + |> restart).startStrict(next: { nextThreadData, nextChatSuggestionTip in + guard let strongSelf = self else { + return + } + + strongSelf.offerNextChannelToRead = true + strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextThreadData.flatMap { nextThreadData -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in + return (peer: EnginePeer(channel), threadData: nextThreadData, unreadCount: Int(nextThreadData.data.incomingUnreadCount), location: .same) + } + strongSelf.chatDisplayNode.historyNode.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3 + + strongSelf.updateNextChannelToReadVisibility() + }) + } + } + if !strongSelf.didSetChatLocationInfoReady { + strongSelf.didSetChatLocationInfoReady = true + strongSelf._chatLocationInfoReady.set(.single(true)) + } + } + })) + } else if case .customChatContents = self.chatLocationInfoData { + self.reportIrrelvantGeoNoticePromise.set(.single(nil)) + self.titleDisposable.set(nil) + + var peerView: Signal = .single(nil) + + if case let .customChatContents(customChatContents) = self.subject { + switch customChatContents.kind { + case .hashTagSearch: + break + case let .quickReplyMessageInput(shortcut, shortcutType): + switch shortcutType { + case .generic: + self.chatTitleView?.titleContent = .custom("\(shortcut)", nil, false) + case .greeting: + self.chatTitleView?.titleContent = .custom(self.presentationData.strings.QuickReply_TitleGreetingMessage, nil, false) + case .away: + self.chatTitleView?.titleContent = .custom(self.presentationData.strings.QuickReply_TitleAwayMessage, nil, false) + } + case let .businessLinkSetup(link): + let linkUrl: String + if link.url.hasPrefix("https://") { + linkUrl = String(link.url[link.url.index(link.url.startIndex, offsetBy: "https://".count)...]) + } else { + linkUrl = link.url + } + + self.chatTitleView?.titleContent = .custom(link.title ?? self.presentationData.strings.Business_Links_EditLinkTitle, linkUrl, false) + case .postSuggestions: + if let customChatContents = customChatContents as? PostSuggestionsChatContents { + peerView = context.account.viewTracker.peerView(customChatContents.peerId) |> map(Optional.init) + } + + //TODO:localize + self.chatTitleView?.titleContent = .custom("Message Suggestions", nil, false) + } + } else { + self.chatTitleView?.titleContent = .custom(" ", nil, false) + } + + self.peerDisposable.set((peerView + |> deliverOnMainQueue).startStrict(next: { [weak self] peerView in + guard let self else { + return + } + + var renderedPeer: RenderedPeer? + if let peerView, let peer = peerView.peers[peerView.peerId] { + var peers = SimpleDictionary() + peers[peer.id] = peer + if let associatedPeerId = peer.associatedPeerId, let associatedPeer = peerView.peers[associatedPeerId] { + peers[associatedPeer.id] = associatedPeer + } + renderedPeer = RenderedPeer(peerId: peer.id, peers: peers, associatedMedia: peerView.media) + + (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: self.context, theme: self.presentationData.theme, peer: EnginePeer(peer), overrideImage: nil) + } + + self.peerView = peerView + + if self.isNodeLoaded { + self.chatDisplayNode.overlayTitle = self.overlayTitle + } + (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = false + + self.updateChatPresentationInterfaceState(animated: false, interactive: false, { + return $0.updatedPeer { _ in + return renderedPeer + }.updatedInterfaceState { interfaceState in + return interfaceState + } + }) + + if !self.didSetChatLocationInfoReady { + self.didSetChatLocationInfoReady = true + self._chatLocationInfoReady.set(.single(true)) + } + })) + } + } + + func reloadCachedData() { + if let peerId = self.chatLocation.peerId { + let customEmojiAvailable: Signal = self.context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.SecretChatLayer(id: peerId) + ) + |> map { layer -> Bool in + guard let layer = layer else { + return true + } + + return layer >= 144 + } + |> distinctUntilChanged + + let isForum = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> map { peer -> Bool in + if case let .channel(channel) = peer { + return channel.isForumOrMonoForum + } else { + return false + } + } + |> distinctUntilChanged + + let context = self.context + let threadData: Signal + let forumTopicData: Signal + if let threadId = self.chatLocation.threadId { + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId) + threadData = context.account.postbox.combinedView(keys: [viewKey]) + |> map { views -> ChatPresentationInterfaceState.ThreadData? in + guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { + return nil + } + guard let data = view.info?.data.get(MessageHistoryThreadData.self) else { + return nil + } + return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) + } + |> distinctUntilChanged + forumTopicData = .single(nil) + } else { + forumTopicData = isForum + |> mapToSignal { isForum -> Signal in + if isForum { + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: 1) + return context.account.postbox.combinedView(keys: [viewKey]) + |> map { views -> ChatPresentationInterfaceState.ThreadData? in + guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { + return nil + } + guard let data = view.info?.data.get(MessageHistoryThreadData.self) else { + return nil + } + return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) + } + |> distinctUntilChanged + } else { + return .single(nil) + } + } + threadData = .single(nil) + } + + if case .standard(.previewing) = self.presentationInterfaceState.mode { + + } else if peerId.namespace != Namespaces.Peer.SecretChat && peerId != context.account.peerId && self.subject != .scheduledMessages { + self.premiumGiftSuggestionDisposable?.dispose() + self.premiumGiftSuggestionDisposable = (ApplicationSpecificNotice.dismissedPremiumGiftSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId) + |> deliverOnMainQueue).startStrict(next: { [weak self] timestamp in + if let strongSelf = self { + let currentTime = Int32(Date().timeIntervalSince1970) + strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in + var suggest = true + if let timestamp, currentTime < timestamp + 60 * 60 * 24 { + suggest = false + } + return state.updatedSuggestPremiumGift(suggest) + }) + } + }) + + var baseLanguageCode = self.presentationData.strings.baseLanguageCode + if baseLanguageCode.contains("-") { + baseLanguageCode = baseLanguageCode.components(separatedBy: "-").first ?? baseLanguageCode + } + let isPremium = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> map { peer -> Bool in + return peer?.isPremium ?? false + } |> distinctUntilChanged + + let isHidden = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.TranslationHidden(id: peerId)) + |> distinctUntilChanged + + let hasAutoTranslate = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.AutoTranslateEnabled(id: peerId)) + |> distinctUntilChanged + + self.translationStateDisposable?.dispose() + let chatLocation = self.chatLocation + self.translationStateDisposable = (combineLatest( + queue: .concurrentDefaultQueue(), + isPremium, + isHidden, + hasAutoTranslate, + ApplicationSpecificNotice.translationSuggestion(accountManager: self.context.sharedContext.accountManager) + ) |> mapToSignal { isPremium, isHidden, hasAutoTranslate, counterAndTimestamp -> Signal in + var maybeSuggestPremium = false + if counterAndTimestamp.0 >= 3 { + maybeSuggestPremium = true + } + if (isPremium || maybeSuggestPremium || hasAutoTranslate) && !isHidden { + return chatTranslationState(context: context, peerId: peerId, threadId: chatLocation.threadId) + |> map { translationState -> ChatPresentationTranslationState? in + if let translationState, !translationState.fromLang.isEmpty && (translationState.fromLang != baseLanguageCode || translationState.isEnabled) { + return ChatPresentationTranslationState(isEnabled: translationState.isEnabled, fromLang: translationState.fromLang, toLang: translationState.toLang ?? baseLanguageCode) + } else { + return nil + } + } + |> distinctUntilChanged + } else { + return .single(nil) + } + } + |> deliverOnMainQueue).startStrict(next: { [weak self] chatTranslationState in + if let strongSelf = self { + strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in + return state.updatedTranslationState(chatTranslationState) + }) + } + }) + } + + let premiumGiftOptions: Signal<[CachedPremiumGiftOption], NoError> = .single([]) + |> then( + self.context.engine.payments.premiumGiftCodeOptions(peerId: peerId, onlyCached: true) + |> map { options in + return options.filter { $0.users == 1 }.map { CachedPremiumGiftOption(months: $0.months, currency: $0.currency, amount: $0.amount, botUrl: "", storeProductId: $0.storeProductId) } + } + ) + + let isTopReplyThreadMessageShown: Signal = self.chatDisplayNode.historyNode.isTopReplyThreadMessageShown.get() + |> distinctUntilChanged + + let hasPendingMessages: Signal + let chatLocationPeerId = self.chatLocation.peerId + + if let chatLocationPeerId = chatLocationPeerId { + hasPendingMessages = self.context.account.pendingMessageManager.hasPendingMessages + |> mapToSignal { peerIds -> Signal in + let value = peerIds.contains(chatLocationPeerId) + if value { + return .single(true) + } else { + return .single(false) + } + } + |> distinctUntilChanged + } else { + hasPendingMessages = .single(false) + } + + let topPinnedMessage: Signal + if let subject = self.subject { + switch subject { + case .messageOptions, .pinnedMessages, .scheduledMessages: + topPinnedMessage = .single(nil) + default: + topPinnedMessage = self.topPinnedMessageSignal(latest: false) + } + } else { + topPinnedMessage = self.topPinnedMessageSignal(latest: false) + } + + self.cachedDataDisposable?.dispose() + self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages |> debug_measureTimeToFirstEvent(label: "cachedData_cachedPeerDataAndMessages"), + hasPendingMessages |> debug_measureTimeToFirstEvent(label: "cachedData_hasPendingMessages"), + isTopReplyThreadMessageShown |> debug_measureTimeToFirstEvent(label: "cachedData_isTopReplyThreadMessageShown"), + topPinnedMessage |> debug_measureTimeToFirstEvent(label: "cachedData_topPinnedMessage"), + customEmojiAvailable |> debug_measureTimeToFirstEvent(label: "cachedData_customEmojiAvailable"), + isForum |> debug_measureTimeToFirstEvent(label: "cachedData_isForum"), + threadData |> debug_measureTimeToFirstEvent(label: "cachedData_threadData"), + forumTopicData |> debug_measureTimeToFirstEvent(label: "cachedData_forumTopicData"), + premiumGiftOptions |> debug_measureTimeToFirstEvent(label: "cachedData_premiumGiftOptions") + ).startStrict(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable, isForum, threadData, forumTopicData, premiumGiftOptions in + if let strongSelf = self { + let (cachedData, messages) = cachedDataAndMessages + + if cachedData != nil { + var themeEmoticon: String? = nil + var chatWallpaper: TelegramWallpaper? + if let cachedData = cachedData as? CachedUserData { + themeEmoticon = cachedData.themeEmoticon + chatWallpaper = cachedData.wallpaper + } else if let cachedData = cachedData as? CachedGroupData { + themeEmoticon = cachedData.themeEmoticon + } else if let cachedData = cachedData as? CachedChannelData { + themeEmoticon = cachedData.themeEmoticon + chatWallpaper = cachedData.wallpaper + } + + strongSelf.chatThemeEmoticonPromise.set(.single(themeEmoticon)) + strongSelf.chatWallpaperPromise.set(.single(chatWallpaper)) + } + + var pinnedMessageId: MessageId? + var peerIsBlocked: Bool = false + var callsAvailable: Bool = false + var callsPrivate: Bool = false + var voiceMessagesAvailable: Bool = true + var slowmodeState: ChatSlowmodeState? + var activeGroupCallInfo: ChatActiveGroupCallInfo? + var inviteRequestsPending: Int32? + if let cachedData = cachedData as? CachedChannelData { + pinnedMessageId = cachedData.pinnedMessageId + if !canBypassRestrictions(chatPresentationInterfaceState: strongSelf.presentationInterfaceState) { + if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout { + if hasPendingMessages { + slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .pendingMessages) + } else if let slowmodeUntilTimestamp = calculateSlowmodeActiveUntilTimestamp(account: strongSelf.context.account, untilTimestamp: cachedData.slowModeValidUntilTimestamp) { + slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .timestamp(slowmodeUntilTimestamp)) + } + } + } + if let activeCall = cachedData.activeCall { + activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) + } + inviteRequestsPending = cachedData.inviteRequestsPending + } else if let cachedData = cachedData as? CachedUserData { + peerIsBlocked = cachedData.isBlocked + callsAvailable = cachedData.voiceCallsAvailable + callsPrivate = cachedData.callsPrivate + pinnedMessageId = cachedData.pinnedMessageId + voiceMessagesAvailable = cachedData.voiceMessagesAvailable + } else if let cachedData = cachedData as? CachedGroupData { + pinnedMessageId = cachedData.pinnedMessageId + if let activeCall = cachedData.activeCall { + activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) + } + inviteRequestsPending = cachedData.inviteRequestsPending + } else if let _ = cachedData as? CachedSecretChatData { + } + + var pinnedMessage: ChatPinnedMessage? + switch strongSelf.chatLocation { + case let .replyThread(replyThreadMessage): + if isForum { + pinnedMessageId = topPinnedMessage?.message.id + pinnedMessage = topPinnedMessage + } else { + if isTopReplyThreadMessageShown { + pinnedMessageId = nil + } else { + pinnedMessageId = replyThreadMessage.effectiveTopId + } + if let pinnedMessageId = pinnedMessageId { + if let message = messages?[pinnedMessageId] { + pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id) + } + } + } + case .peer: + pinnedMessageId = topPinnedMessage?.message.id + pinnedMessage = topPinnedMessage + case .customChatContents: + pinnedMessageId = nil + pinnedMessage = nil + } + + var pinnedMessageUpdated = false + if let current = strongSelf.presentationInterfaceState.pinnedMessage, let updated = pinnedMessage { + if current != updated { + pinnedMessageUpdated = true + } + } else if (strongSelf.presentationInterfaceState.pinnedMessage != nil) != (pinnedMessage != nil) { + pinnedMessageUpdated = true + } + + let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate + + let voiceMessagesAvailableUpdated = strongSelf.presentationInterfaceState.voiceMessagesAvailable != voiceMessagesAvailable + + var canManageInvitations = false + if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { + canManageInvitations = true + } else if let group = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup { + if case .creator = group.role { + canManageInvitations = true + } else if case let .admin(rights, _) = group.role, rights.rights.contains(.canInviteUsers) { + canManageInvitations = true + } + } + + if canManageInvitations, let inviteRequestsPending = inviteRequestsPending, inviteRequestsPending >= 0 { + if strongSelf.inviteRequestsContext == nil { + let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil)) + strongSelf.inviteRequestsContext = inviteRequestsContext + + strongSelf.inviteRequestsDisposable.set((combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId))).startStrict(next: { [weak self] requestsState, dismissedInvitationRequests in + guard let strongSelf = self else { + return + } + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { state in + return state + .updatedTitlePanelContext({ context in + let peers: [EnginePeer] = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3)) + + var peersDismissed = false + if let dismissedInvitationRequests = dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) { + peersDismissed = true + } + + if requestsState.count > 0 && !peersDismissed { + if !context.contains(where: { + switch $0 { + case .inviteRequests(peers, requestsState.count): + return true + default: + return false + } + }) { + var updatedContexts = context.filter { c in + if case .inviteRequests = c { + return false + } else { + return true + } + } + updatedContexts.append(.inviteRequests(peers, requestsState.count)) + return updatedContexts.sorted() + } else { + return context + } + } else { + if let index = context.firstIndex(where: { + switch $0 { + case .inviteRequests: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.remove(at: index) + return updatedContexts + } else { + return context + } + } + }) + .updatedSlowmodeState(slowmodeState) + }) + })) + } else if let inviteRequestsContext = strongSelf.inviteRequestsContext { + let _ = (inviteRequestsContext.state + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak inviteRequestsContext] state in + if state.count != inviteRequestsPending { + inviteRequestsContext?.loadMore() + } + }) + } + } + + if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || voiceMessagesAvailableUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo || customEmojiAvailable != strongSelf.presentationInterfaceState.customEmojiAvailable || threadData != strongSelf.presentationInterfaceState.threadData || forumTopicData != strongSelf.presentationInterfaceState.forumTopicData || premiumGiftOptions != strongSelf.presentationInterfaceState.premiumGiftOptions { + strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in + return state + .updatedPinnedMessageId(pinnedMessageId) + .updatedActiveGroupCallInfo(activeGroupCallInfo) + .updatedPinnedMessage(pinnedMessage) + .updatedPeerIsBlocked(peerIsBlocked) + .updatedCallsAvailable(callsAvailable) + .updatedCallsPrivate(callsPrivate) + .updatedVoiceMessagesAvailable(voiceMessagesAvailable) + .updatedCustomEmojiAvailable(customEmojiAvailable) + .updatedThreadData(threadData) + .updatedForumTopicData(forumTopicData) + .updatedIsGeneralThreadClosed(forumTopicData?.isClosed) + .updatedPremiumGiftOptions(premiumGiftOptions) + .updatedTitlePanelContext({ context in + if pinnedMessageId != nil { + if !context.contains(where: { + switch $0 { + case .pinnedMessage: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.append(.pinnedMessage) + return updatedContexts.sorted() + } else { + return context + } + } else { + if let index = context.firstIndex(where: { + switch $0 { + case .pinnedMessage: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.remove(at: index) + return updatedContexts + } else { + return context + } + } + }) + .updatedSlowmodeState(slowmodeState) + }) + } + + if !strongSelf.didSetCachedDataReady { + strongSelf.didSetCachedDataReady = true + strongSelf.cachedDataReady.set(.single(true)) + } + } + }) + } else { + if !self.didSetCachedDataReady { + self.didSetCachedDataReady = true + self.cachedDataReady.set(.single(true)) + } + } + } + func loadDisplayNodeImpl() { if #available(iOS 18.0, *) { if self.context.sharedContext.immediateExperimentalUISettings.enableLocalTranslation { @@ -428,7 +2464,7 @@ extension ChatControllerImpl { } if case let .replyThread(replyThreadMessageId) = strongSelf.chatLocation { - if let channel = combinedInitialData.initialData?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = combinedInitialData.initialData?.peer as? TelegramChannel, channel.isForumOrMonoForum { pinnedMessageId = nil } else { pinnedMessageId = replyThreadMessageId.effectiveTopId @@ -543,39 +2579,6 @@ extension ChatControllerImpl { } }) - let hasPendingMessages: Signal - let chatLocationPeerId = self.chatLocation.peerId - - if let chatLocationPeerId = chatLocationPeerId { - hasPendingMessages = self.context.account.pendingMessageManager.hasPendingMessages - |> mapToSignal { peerIds -> Signal in - let value = peerIds.contains(chatLocationPeerId) - if value { - return .single(true) - } else { - return .single(false) - } - } - |> distinctUntilChanged - } else { - hasPendingMessages = .single(false) - } - - let isTopReplyThreadMessageShown: Signal = self.chatDisplayNode.historyNode.isTopReplyThreadMessageShown.get() - |> distinctUntilChanged - - let topPinnedMessage: Signal - if let subject = self.subject { - switch subject { - case .messageOptions, .pinnedMessages, .scheduledMessages: - topPinnedMessage = .single(nil) - default: - topPinnedMessage = self.topPinnedMessageSignal(latest: false) - } - } else { - topPinnedMessage = self.topPinnedMessageSignal(latest: false) - } - if let peerId = self.chatLocation.peerId { self.chatThemeEmoticonPromise.set(self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThemeEmoticon(id: peerId))) let chatWallpaper = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Wallpaper(id: peerId)) @@ -586,397 +2589,7 @@ extension ChatControllerImpl { self.chatWallpaperPromise.set(.single(nil)) } - if let peerId = self.chatLocation.peerId { - let customEmojiAvailable: Signal = self.context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Peer.SecretChatLayer(id: peerId) - ) - |> map { layer -> Bool in - guard let layer = layer else { - return true - } - - return layer >= 144 - } - |> distinctUntilChanged - - let isForum = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> map { peer -> Bool in - if case let .channel(channel) = peer { - return channel.flags.contains(.isForum) - } else { - return false - } - } - |> distinctUntilChanged - - let context = self.context - let threadData: Signal - let forumTopicData: Signal - if let threadId = self.chatLocation.threadId { - let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId) - threadData = context.account.postbox.combinedView(keys: [viewKey]) - |> map { views -> ChatPresentationInterfaceState.ThreadData? in - guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { - return nil - } - guard let data = view.info?.data.get(MessageHistoryThreadData.self) else { - return nil - } - return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) - } - |> distinctUntilChanged - forumTopicData = .single(nil) - } else { - forumTopicData = isForum - |> mapToSignal { isForum -> Signal in - if isForum { - let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: 1) - return context.account.postbox.combinedView(keys: [viewKey]) - |> map { views -> ChatPresentationInterfaceState.ThreadData? in - guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { - return nil - } - guard let data = view.info?.data.get(MessageHistoryThreadData.self) else { - return nil - } - return ChatPresentationInterfaceState.ThreadData(title: data.info.title, icon: data.info.icon, iconColor: data.info.iconColor, isOwnedByMe: data.isOwnedByMe, isClosed: data.isClosed) - } - |> distinctUntilChanged - } else { - return .single(nil) - } - } - threadData = .single(nil) - } - - if case .standard(.previewing) = self.presentationInterfaceState.mode { - - } else if peerId.namespace != Namespaces.Peer.SecretChat && peerId != context.account.peerId && self.subject != .scheduledMessages { - self.premiumGiftSuggestionDisposable = (ApplicationSpecificNotice.dismissedPremiumGiftSuggestion(accountManager: self.context.sharedContext.accountManager, peerId: peerId) - |> deliverOnMainQueue).startStrict(next: { [weak self] timestamp in - if let strongSelf = self { - let currentTime = Int32(Date().timeIntervalSince1970) - strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in - var suggest = true - if let timestamp, currentTime < timestamp + 60 * 60 * 24 { - suggest = false - } - return state.updatedSuggestPremiumGift(suggest) - }) - } - }) - - var baseLanguageCode = self.presentationData.strings.baseLanguageCode - if baseLanguageCode.contains("-") { - baseLanguageCode = baseLanguageCode.components(separatedBy: "-").first ?? baseLanguageCode - } - let isPremium = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> map { peer -> Bool in - return peer?.isPremium ?? false - } |> distinctUntilChanged - - let isHidden = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.TranslationHidden(id: peerId)) - |> distinctUntilChanged - - let hasAutoTranslate = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.AutoTranslateEnabled(id: peerId)) - |> distinctUntilChanged - - let chatLocation = self.chatLocation - self.translationStateDisposable = (combineLatest( - queue: .concurrentDefaultQueue(), - isPremium, - isHidden, - hasAutoTranslate, - ApplicationSpecificNotice.translationSuggestion(accountManager: self.context.sharedContext.accountManager) - ) |> mapToSignal { isPremium, isHidden, hasAutoTranslate, counterAndTimestamp -> Signal in - var maybeSuggestPremium = false - if counterAndTimestamp.0 >= 3 { - maybeSuggestPremium = true - } - if (isPremium || maybeSuggestPremium || hasAutoTranslate) && !isHidden { - return chatTranslationState(context: context, peerId: peerId, threadId: chatLocation.threadId) - |> map { translationState -> ChatPresentationTranslationState? in - if let translationState, !translationState.fromLang.isEmpty && (translationState.fromLang != baseLanguageCode || translationState.isEnabled) { - return ChatPresentationTranslationState(isEnabled: translationState.isEnabled, fromLang: translationState.fromLang, toLang: translationState.toLang ?? baseLanguageCode) - } else { - return nil - } - } - |> distinctUntilChanged - } else { - return .single(nil) - } - } - |> deliverOnMainQueue).startStrict(next: { [weak self] chatTranslationState in - if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in - return state.updatedTranslationState(chatTranslationState) - }) - } - }) - } - - let premiumGiftOptions: Signal<[CachedPremiumGiftOption], NoError> = .single([]) - |> then( - self.context.engine.payments.premiumGiftCodeOptions(peerId: peerId, onlyCached: true) - |> map { options in - return options.filter { $0.users == 1 }.map { CachedPremiumGiftOption(months: $0.months, currency: $0.currency, amount: $0.amount, botUrl: "", storeProductId: $0.storeProductId) } - } - ) - - self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages |> debug_measureTimeToFirstEvent(label: "cachedData_cachedPeerDataAndMessages"), - hasPendingMessages |> debug_measureTimeToFirstEvent(label: "cachedData_hasPendingMessages"), - isTopReplyThreadMessageShown |> debug_measureTimeToFirstEvent(label: "cachedData_isTopReplyThreadMessageShown"), - topPinnedMessage |> debug_measureTimeToFirstEvent(label: "cachedData_topPinnedMessage"), - customEmojiAvailable |> debug_measureTimeToFirstEvent(label: "cachedData_customEmojiAvailable"), - isForum |> debug_measureTimeToFirstEvent(label: "cachedData_isForum"), - threadData |> debug_measureTimeToFirstEvent(label: "cachedData_threadData"), - forumTopicData |> debug_measureTimeToFirstEvent(label: "cachedData_forumTopicData"), - premiumGiftOptions |> debug_measureTimeToFirstEvent(label: "cachedData_premiumGiftOptions") - ).startStrict(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable, isForum, threadData, forumTopicData, premiumGiftOptions in - if let strongSelf = self { - let (cachedData, messages) = cachedDataAndMessages - - if cachedData != nil { - var themeEmoticon: String? = nil - var chatWallpaper: TelegramWallpaper? - if let cachedData = cachedData as? CachedUserData { - themeEmoticon = cachedData.themeEmoticon - chatWallpaper = cachedData.wallpaper - } else if let cachedData = cachedData as? CachedGroupData { - themeEmoticon = cachedData.themeEmoticon - } else if let cachedData = cachedData as? CachedChannelData { - themeEmoticon = cachedData.themeEmoticon - chatWallpaper = cachedData.wallpaper - } - - strongSelf.chatThemeEmoticonPromise.set(.single(themeEmoticon)) - strongSelf.chatWallpaperPromise.set(.single(chatWallpaper)) - } - - var pinnedMessageId: MessageId? - var peerIsBlocked: Bool = false - var callsAvailable: Bool = false - var callsPrivate: Bool = false - var voiceMessagesAvailable: Bool = true - var slowmodeState: ChatSlowmodeState? - var activeGroupCallInfo: ChatActiveGroupCallInfo? - var inviteRequestsPending: Int32? - if let cachedData = cachedData as? CachedChannelData { - pinnedMessageId = cachedData.pinnedMessageId - if !canBypassRestrictions(chatPresentationInterfaceState: strongSelf.presentationInterfaceState) { - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout { - if hasPendingMessages { - slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .pendingMessages) - } else if let slowmodeUntilTimestamp = calculateSlowmodeActiveUntilTimestamp(account: strongSelf.context.account, untilTimestamp: cachedData.slowModeValidUntilTimestamp) { - slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .timestamp(slowmodeUntilTimestamp)) - } - } - } - if let activeCall = cachedData.activeCall { - activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) - } - inviteRequestsPending = cachedData.inviteRequestsPending - } else if let cachedData = cachedData as? CachedUserData { - peerIsBlocked = cachedData.isBlocked - callsAvailable = cachedData.voiceCallsAvailable - callsPrivate = cachedData.callsPrivate - pinnedMessageId = cachedData.pinnedMessageId - voiceMessagesAvailable = cachedData.voiceMessagesAvailable - } else if let cachedData = cachedData as? CachedGroupData { - pinnedMessageId = cachedData.pinnedMessageId - if let activeCall = cachedData.activeCall { - activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) - } - inviteRequestsPending = cachedData.inviteRequestsPending - } else if let _ = cachedData as? CachedSecretChatData { - } - - var pinnedMessage: ChatPinnedMessage? - switch strongSelf.chatLocation { - case let .replyThread(replyThreadMessage): - if isForum { - pinnedMessageId = topPinnedMessage?.message.id - pinnedMessage = topPinnedMessage - } else { - if isTopReplyThreadMessageShown { - pinnedMessageId = nil - } else { - pinnedMessageId = replyThreadMessage.effectiveTopId - } - if let pinnedMessageId = pinnedMessageId { - if let message = messages?[pinnedMessageId] { - pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id) - } - } - } - case .peer: - pinnedMessageId = topPinnedMessage?.message.id - pinnedMessage = topPinnedMessage - case .customChatContents: - pinnedMessageId = nil - pinnedMessage = nil - } - - var pinnedMessageUpdated = false - if let current = strongSelf.presentationInterfaceState.pinnedMessage, let updated = pinnedMessage { - if current != updated { - pinnedMessageUpdated = true - } - } else if (strongSelf.presentationInterfaceState.pinnedMessage != nil) != (pinnedMessage != nil) { - pinnedMessageUpdated = true - } - - let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate - - let voiceMessagesAvailableUpdated = strongSelf.presentationInterfaceState.voiceMessagesAvailable != voiceMessagesAvailable - - var canManageInvitations = false - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { - canManageInvitations = true - } else if let group = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup { - if case .creator = group.role { - canManageInvitations = true - } else if case let .admin(rights, _) = group.role, rights.rights.contains(.canInviteUsers) { - canManageInvitations = true - } - } - - if canManageInvitations, let inviteRequestsPending = inviteRequestsPending, inviteRequestsPending >= 0 { - if strongSelf.inviteRequestsContext == nil { - let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil)) - strongSelf.inviteRequestsContext = inviteRequestsContext - - strongSelf.inviteRequestsDisposable.set((combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId))).startStrict(next: { [weak self] requestsState, dismissedInvitationRequests in - guard let strongSelf = self else { - return - } - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { state in - return state - .updatedTitlePanelContext({ context in - let peers: [EnginePeer] = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3)) - - var peersDismissed = false - if let dismissedInvitationRequests = dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) { - peersDismissed = true - } - - if requestsState.count > 0 && !peersDismissed { - if !context.contains(where: { - switch $0 { - case .inviteRequests(peers, requestsState.count): - return true - default: - return false - } - }) { - var updatedContexts = context.filter { c in - if case .inviteRequests = c { - return false - } else { - return true - } - } - updatedContexts.append(.inviteRequests(peers, requestsState.count)) - return updatedContexts.sorted() - } else { - return context - } - } else { - if let index = context.firstIndex(where: { - switch $0 { - case .inviteRequests: - return true - default: - return false - } - }) { - var updatedContexts = context - updatedContexts.remove(at: index) - return updatedContexts - } else { - return context - } - } - }) - .updatedSlowmodeState(slowmodeState) - }) - })) - } else if let inviteRequestsContext = strongSelf.inviteRequestsContext { - let _ = (inviteRequestsContext.state - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak inviteRequestsContext] state in - if state.count != inviteRequestsPending { - inviteRequestsContext?.loadMore() - } - }) - } - } - - if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || voiceMessagesAvailableUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo || customEmojiAvailable != strongSelf.presentationInterfaceState.customEmojiAvailable || threadData != strongSelf.presentationInterfaceState.threadData || forumTopicData != strongSelf.presentationInterfaceState.forumTopicData || premiumGiftOptions != strongSelf.presentationInterfaceState.premiumGiftOptions { - strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in - return state - .updatedPinnedMessageId(pinnedMessageId) - .updatedActiveGroupCallInfo(activeGroupCallInfo) - .updatedPinnedMessage(pinnedMessage) - .updatedPeerIsBlocked(peerIsBlocked) - .updatedCallsAvailable(callsAvailable) - .updatedCallsPrivate(callsPrivate) - .updatedVoiceMessagesAvailable(voiceMessagesAvailable) - .updatedCustomEmojiAvailable(customEmojiAvailable) - .updatedThreadData(threadData) - .updatedForumTopicData(forumTopicData) - .updatedIsGeneralThreadClosed(forumTopicData?.isClosed) - .updatedPremiumGiftOptions(premiumGiftOptions) - .updatedTitlePanelContext({ context in - if pinnedMessageId != nil { - if !context.contains(where: { - switch $0 { - case .pinnedMessage: - return true - default: - return false - } - }) { - var updatedContexts = context - updatedContexts.append(.pinnedMessage) - return updatedContexts.sorted() - } else { - return context - } - } else { - if let index = context.firstIndex(where: { - switch $0 { - case .pinnedMessage: - return true - default: - return false - } - }) { - var updatedContexts = context - updatedContexts.remove(at: index) - return updatedContexts - } else { - return context - } - } - }) - .updatedSlowmodeState(slowmodeState) - }) - } - - if !strongSelf.didSetCachedDataReady { - strongSelf.didSetCachedDataReady = true - strongSelf.cachedDataReady.set(.single(true)) - } - } - }) - } else { - if !self.didSetCachedDataReady { - self.didSetCachedDataReady = true - self.cachedDataReady.set(.single(true)) - } - } + self.reloadCachedData() self.historyStateDisposable = self.chatDisplayNode.historyNode.historyState.get().startStrict(next: { [weak self] state in if let strongSelf = self { @@ -4131,15 +5744,36 @@ extension ChatControllerImpl { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) } }, openSuggestPost: { [weak self] in - let _ = self - /*guard let self else { + guard let self else { return - } - guard let peerId = self.chatLocation.peerId else { + } + guard let peerId = self.chatLocation.peerId else { return - } - - let contents = PostSuggestionsChatContents( + } + + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.LinkedMonoforumPeerId(id: peerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] monoforumPeerIdValue in + guard let self, case let .known(monoforumPeerIdValue) = monoforumPeerIdValue, let monoforumPeerId = monoforumPeerIdValue else { + return + } + + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: monoforumPeerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] monoforumPeer in + guard let self, let monoforumPeer else { + return + } + guard let navigationController = self.effectiveNavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(monoforumPeer), keepStack: .always)) + }) + }) + + /*let contents = PostSuggestionsChatContents( context: self.context, peerId: peerId ) @@ -4590,6 +6224,58 @@ extension ChatControllerImpl { } else { apply() } + }, updateChatLocationThread: { [weak self] threadId in + guard let self else { + return + } + guard let peerId = self.chatLocation.peerId else { + return + } + guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else { + return + } + let updatedChatLocation: ChatLocation + if let threadId { + var isMonoforum = false + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + isMonoforum = true + } + + updatedChatLocation = .replyThread(message: ChatReplyThreadMessage( + peerId: peerId, + threadId: threadId, + channelMessageId: nil, + isChannelPost: false, + isForumPost: true, + isMonoforumPost: isMonoforum, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )) + } else { + updatedChatLocation = .peer(id: peerId) + } + self.updateChatPresentationInterfaceState(animated: true, interactive: false, { presentationInterfaceState in + return presentationInterfaceState.updatedChatLocation(updatedChatLocation) + }) + }, toggleChatSidebarMode: { [weak self] in + guard let self else { + return + } + self.updateChatPresentationInterfaceState(animated: true, interactive: true, { presentationInterfaceState in + let topicListDisplayMode: ChatPresentationInterfaceState.TopicListDisplayMode + switch presentationInterfaceState.topicListDisplayMode ?? .top { + case .top: + topicListDisplayMode = .side + case .side: + topicListDisplayMode = .top + } + return presentationInterfaceState.updatedTopicListDisplayMode(topicListDisplayMode) + }) }, updateDisplayHistoryFilterAsList: { [weak self] displayAsList in guard let self else { return @@ -5032,6 +6718,7 @@ extension ChatControllerImpl { channelMessageId: nil, isChannelPost: false, isForumPost: true, + isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift index ebd1d447e5..832c7fe53f 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift @@ -143,8 +143,8 @@ extension ChatControllerImpl { self.dismiss() let navigateToLocation: NavigateToChatControllerParams.Location - if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { - navigateToLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum { + navigateToLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { navigateToLocation = .peer(peer) } @@ -177,9 +177,9 @@ extension ChatControllerImpl { var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) var preloadChatLocation: ChatLocation = .peer(id: peer.id) var displayMessageNotFoundToast = false - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { if let message = message, let threadId = message.threadId { - let replyThreadMessage = ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) + let replyThreadMessage = ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) chatLocation = .replyThread(replyThreadMessage) preloadChatLocation = .replyThread(message: replyThreadMessage) } else { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift index 1ee57f9110..8e4780d163 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift @@ -426,7 +426,7 @@ extension ChatControllerImpl { if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: .forumTopic(thread: replyThreadMessage), avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) { self.effectiveNavigationController?.pushViewController(infoController) } - } else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case let .replyThread(message) = self.chatLocation { + } else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, case let .replyThread(message) = self.chatLocation { if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: channel, mode: .forumTopic(thread: message), avatarInitiallyExpanded: false, fromChat: true, requestsContext: self.inviteRequestsContext) { self.effectiveNavigationController?.pushViewController(infoController) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift index 9c59b2978c..2509fa7495 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift @@ -224,7 +224,7 @@ extension ChatControllerImpl { } }) } else { - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { self.effectiveNavigationController?.pushViewController(ChatListControllerImpl(context: self.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, enableDebugActions: false)) } else { self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peer.id), subject: subject)) diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 86bdb84012..14ae0ce885 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -591,6 +591,7 @@ func updateChatPresentationInterfaceStateImpl( if selfController.chatDisplayNode.historyNode.chatLocation != selfController.presentationInterfaceState.chatLocation { selfController.chatLocation = selfController.presentationInterfaceState.chatLocation + selfController.chatDisplayNode.chatLocation = selfController.presentationInterfaceState.chatLocation selfController.reloadChatLocation() selfController.reloadCachedData() selfController.chatDisplayNode.historyNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index afba1eb742..6770d41ca7 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3044,7 +3044,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return .none } } - if case .peer = strongSelf.chatLocation, let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if case .peer = strongSelf.chatLocation, let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { if message.threadId == nil { return .none } @@ -6131,7 +6131,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let chatLocation: ChatLocation if let threadId { - chatLocation = .replyThread(message: ChatReplyThreadMessage(peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + chatLocation = .replyThread(message: ChatReplyThreadMessage(peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { chatLocation = .peer(id: peerId) } @@ -7032,7 +7032,7 @@ 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) { + if case .peer = self.chatLocation, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()).withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in @@ -8610,7 +8610,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self?.playShakeAnimation() } } else if let navigationController = strongSelf.effectiveNavigationController { - if case let .channel(channel) = peerId, channel.flags.contains(.isForum) { + if case let .channel(channel) = peerId, channel.isForumOrMonoForum { strongSelf.context.sharedContext.navigateToForumChannel(context: strongSelf.context, peerId: peerId.id, navigationController: navigationController) } else { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, updateTextInputState: !peerId.id.isGroupOrChannel ? textInputState : nil, keepStack: .always, peekData: peekData)) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 072ca9beef..1e8ec3690c 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -135,7 +135,7 @@ class HistoryNodeContainer: ASDisplayNode { class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { let context: AccountContext - let chatLocation: ChatLocation + var chatLocation: ChatLocation let controllerInteraction: ChatControllerInteraction private weak var controller: ChatControllerImpl? @@ -2964,7 +2964,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { channelMessageId: nil, isChannelPost: false, isForumPost: false, - isMonoforum: false, + isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index 017d71066c..9884183341 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -102,7 +102,7 @@ func chatHistoryViewForLocation( if tag != nil { requestAroundId = true } - if case let .replyThread(message) = chatLocation, (message.peerId == context.account.peerId || message.isMonoforum) { + if case let .replyThread(message) = chatLocation, (message.peerId == context.account.peerId || message.isMonoforumPost) { preFixedReadState = .peer([:]) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index a1650a467d..6d82545c2b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -315,7 +315,7 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS } } - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { if let threadData = chatPresentationInterfaceState.threadData { if threadData.isClosed { var canManage = false @@ -722,7 +722,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState let message = messages[0] - if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { if message.threadId == nil { canReply = false } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index 015d6242d2..27fe1a43f4 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -340,22 +340,6 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState } } } - - if channel.flags.contains(.isForum) { - /*if let _ = chatPresentationInterfaceState.threadData { - } else { - if chatPresentationInterfaceState.interfaceState.replyMessageSubject == nil { - if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) { - return (currentPanel, nil) - } else { - let panel = ChatRestrictedInputPanelNode() - panel.context = context - panel.interfaceInteraction = interfaceInteraction - return (panel, nil) - } - } - }*/ - } } else if let group = peer as? TelegramGroup { switch group.membership { case .Removed, .Left: diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index ffacc12883..5c40500cb1 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -99,7 +99,7 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present } } - if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), let moreInfoNavigationButton = moreInfoNavigationButton { + if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let moreInfoNavigationButton = moreInfoNavigationButton { if case .replyThread = presentationInterfaceState.chatLocation { } else { if case .pinnedMessages = presentationInterfaceState.subject { @@ -155,7 +155,7 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present } if case .replyThread = presentationInterfaceState.chatLocation { - if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { } else if hasMessages { if case .search = currentButton?.action { return currentButton diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 09625c3081..8664b715c8 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -120,7 +120,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat } } - if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { if let threadData = chatPresentationInterfaceState.threadData { if threadData.isClosed { var canManage = false @@ -167,7 +167,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat } } - if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId { if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { @@ -252,7 +252,7 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState } - if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top if case .side = topicListDisplayMode { return AnyComponentWithIdentity( diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index adedeb9680..e831513195 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -103,7 +103,7 @@ private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReport } } } else if let peer = state.renderedPeer?.chatMainPeer { - if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { if let threadData = state.threadData { if threadData.isClosed { var canManage = false diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index f1a51a5c8a..aca5e3f05b 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -98,7 +98,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { } else if let threadData = interfaceState.threadData, threadData.isClosed { iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme) self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelTopicClosedText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) - } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case .peer = interfaceState.chatLocation { + } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, case .peer = interfaceState.chatLocation { if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_TopicIsClosedLabel(threadInfo.title).string, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index d92b509e98..e31573f32c 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -539,6 +539,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch var customEmojiContainerView: CustomEmojiContainerView? let textInputBackgroundNode: ASImageNode + var textInputBackgroundTapRecognizer: TouchDownGestureRecognizer? private var transparentTextInputBackgroundImage: UIImage? let actionButtons: ChatTextInputActionButtonsNode private let slowModeButton: BoostSlowModeButton @@ -1089,6 +1090,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch return false } } + self.textInputBackgroundTapRecognizer = recognizer self.textInputBackgroundNode.isUserInteractionEnabled = true self.textInputBackgroundNode.view.addGestureRecognizer(recognizer) @@ -1166,6 +1168,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch textInputNode.isUserInteractionEnabled = !self.sendingTextDisabled self.textInputNode = textInputNode + if let textInputBackgroundTapRecognizer = self.textInputBackgroundTapRecognizer { + self.textInputBackgroundTapRecognizer = nil + self.textInputBackgroundNode.view.removeGestureRecognizer(textInputBackgroundTapRecognizer) + } + var accessoryButtonsWidth: CGFloat = 0.0 var firstButton = true for (_, button) in self.accessoryItemButtons { @@ -1916,7 +1923,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } else { placeholder = interfaceState.strings.Conversation_InputTextPlaceholderReply } - } else if let channel = peer as? TelegramChannel, channel.isForum, let forumTopicData = interfaceState.forumTopicData { + } else if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum, let forumTopicData = interfaceState.forumTopicData { if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { placeholder = interfaceState.strings.Chat_InputPlaceholderReplyInTopic(threadInfo.title).string } else { diff --git a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift index 6435b9bd7e..393f250524 100644 --- a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift @@ -13,6 +13,7 @@ import Postbox import EmojiStatusComponent import SwiftSignalKit import BundleIconComponent +import AvatarNode final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode, ASScrollViewDelegate { private struct Params: Equatable { @@ -79,6 +80,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C private let containerButton: HighlightTrackingButton private let icon = ComponentView() + private var avatarNode: AvatarNode? private let title = ComponentView() init(context: AccountContext, action: @escaping (() -> Void), contextGesture: @escaping (ContextGesture, ContextExtractedContentContainingNode) -> Void) { @@ -172,7 +174,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C containerSize: CGSize(width: 18.0, height: 18.0) ) - let titleText: String = item.item.threadData?.info.title ?? "Topic" + let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " " let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( @@ -194,6 +196,33 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C self.containerButton.addSubview(iconView) } iconView.frame = iconFrame + + if "".isEmpty { + iconView.isHidden = true + + let avatarNode: AvatarNode + if let current = self.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 7.0)) + self.avatarNode = avatarNode + self.containerButton.addSubview(avatarNode.view) + } + avatarNode.frame = iconFrame + avatarNode.updateSize(size: iconFrame.size) + + if let peer = item.item.renderedPeer.chatMainPeer { + if peer.smallProfileImage != nil { + avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) + } else { + avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) + } + } + } else if let avatarNode = self.avatarNode { + self.avatarNode = nil + avatarNode.view.removeFromSuperview() + iconView.isHidden = false + } } if let titleView = self.title.view { @@ -494,123 +523,54 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C self.scrollView.disablesInteractiveTransitionGestureRecognizer = true - let viewKey: PostboxViewKey = .messageHistoryThreadIndex( - id: peerId, - summaryComponents: ChatListEntrySummaryComponents( - components: [ - ChatListEntryMessageTagSummaryKey( - tag: .unseenPersonalMessage, - actionType: PendingMessageActionType.consumeUnseenPersonalMessage - ): ChatListEntrySummaryComponents.Component( - tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), - actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) - ), - ChatListEntryMessageTagSummaryKey( - tag: .unseenReaction, - actionType: PendingMessageActionType.readReaction - ): ChatListEntrySummaryComponents.Component( - tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), - actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) - ) - ] - ) - ) + let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId) + let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId) - let readStateKey: PostboxViewKey = .combinedReadState(peerId: peerId, handleThreads: false) - - let threadListSignal: Signal = context.account.postbox.combinedView(keys: [viewKey, readStateKey]) + let accountPeerId = context.account.peerId + let threadListSignal: Signal = context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey]) |> map { views -> EngineChatList in - guard let view = views.views[viewKey] as? MessageHistoryThreadIndexView else { - preconditionFailure() - } - guard let readStateView = views.views[readStateKey] as? CombinedReadStateView else { + guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { preconditionFailure() } - var maxReadId: Int32 = 0 - if let state = readStateView.state?.states.first(where: { $0.0 == Namespaces.Message.Cloud }) { - if case let .idBased(maxIncomingReadId, _, _, _, _) = state.1 { - maxReadId = maxIncomingReadId - } - } - - var items: [EngineChatList.Item] = [] - for item in view.items { - guard let peer = view.peer else { - continue - } - guard let data = item.info.get(MessageHistoryThreadData.self) else { - continue - } - - let defaultPeerNotificationSettings: TelegramPeerNotificationSettings = (view.peerNotificationSettings as? TelegramPeerNotificationSettings) ?? .defaultSettings - - var hasUnseenMentions = false - - var isMuted = false - switch data.notificationSettings.muteState { - case .muted: - isMuted = true - case .unmuted: - isMuted = false - case .default: - if case .default = data.notificationSettings.muteState { - if case .muted = defaultPeerNotificationSettings.muteState { - isMuted = true - } - } - } - - if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey( - tag: .unseenPersonalMessage, - actionType: PendingMessageActionType.consumeUnseenPersonalMessage - )] { - hasUnseenMentions = (info.tagSummaryCount ?? 0) > (info.actionsSummaryCount ?? 0) - } - - var hasUnseenReactions = false - if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey( - tag: .unseenReaction, - actionType: PendingMessageActionType.readReaction - )] { - hasUnseenReactions = (info.tagSummaryCount ?? 0) != 0// > (info.actionsSummaryCount ?? 0) - } - - let pinnedIndex: EngineChatList.Item.PinnedIndex - if let index = item.pinnedIndex { - pinnedIndex = .index(index) - } else { - pinnedIndex = .none - } - - var topicMaxIncomingReadId = data.maxIncomingReadId - if data.maxIncomingReadId == 0 && maxReadId != 0 && Int64(maxReadId) <= item.id { - topicMaxIncomingReadId = max(topicMaxIncomingReadId, maxReadId) - } - - let readCounters = EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: topicMaxIncomingReadId, maxOutgoingReadId: data.maxOutgoingReadId, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))]), isMuted: false) - - var draft: EngineChatList.Draft? - if let embeddedState = item.embeddedInterfaceState, let _ = embeddedState.overrideChatTimestamp { + var draft: EngineChatList.Draft? + if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView { + if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp { if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { if let text = opaqueState.synchronizeableInputState?.text { draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? []) } } } + } + + var items: [EngineChatList.Item] = [] + for item in view.items { + guard let sourcePeer = item.peer else { + continue + } + + let sourceId = PeerId(item.id) + + var messages: [EngineMessage] = [] + if let topMessage = item.topMessage { + messages.append(EngineMessage(topMessage)) + } + + let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) items.append(EngineChatList.Item( - id: .forum(item.id), - index: .forum(pinnedIndex: pinnedIndex, timestamp: item.index.timestamp, threadId: item.id, namespace: item.index.id.namespace, id: item.index.id.id), - messages: item.topMessage.flatMap { [EngineMessage($0)] } ?? [], - readCounters: readCounters, - isMuted: isMuted, - draft: draft, - threadData: data, - renderedPeer: EngineRenderedPeer(peer: EnginePeer(peer)), + id: .chatList(sourceId), + index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), + messages: messages, + readCounters: nil, + isMuted: false, + draft: sourceId == accountPeerId ? draft : nil, + threadData: nil, + renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), presence: nil, - hasUnseenMentions: hasUnseenMentions, - hasUnseenReactions: hasUnseenReactions, + hasUnseenMentions: false, + hasUnseenReactions: false, forumTopicData: nil, topForumTopicItems: [], hasFailed: false, @@ -631,6 +591,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C hasLater: false, isLoading: view.isLoading ) + return list } @@ -811,9 +772,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C guard let self else { return } - guard case let .forum(topicId) = chatListItem.id else { - return - } + let topicId = chatListItem.renderedPeer.peerId.toInt64() self.interfaceInteraction?.updateChatLocationThread(topicId) }, contextGesture: { gesture, sourceNode in }) @@ -822,7 +781,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C } var isSelected = false - if case let .forum(topicId) = item.item.id, params.interfaceState.chatLocation.threadId == topicId { + if params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64() { isSelected = true } let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) diff --git a/submodules/TelegramUI/Sources/WidgetDataContext.swift b/submodules/TelegramUI/Sources/WidgetDataContext.swift index bec08ce9e6..31069a815d 100644 --- a/submodules/TelegramUI/Sources/WidgetDataContext.swift +++ b/submodules/TelegramUI/Sources/WidgetDataContext.swift @@ -229,7 +229,7 @@ final class WidgetDataContext { } var isForum = false - if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) { + if let peer = peer as? TelegramChannel, peer.isForumOrMonoForum { isForum = true } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 9318261517..617577eaca 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -822,7 +822,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) }) } case let .channelMessage(id, timecode): - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { let messageId = MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: id) return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false)) |> `catch` { _ in @@ -841,7 +841,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -858,7 +858,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) case let .replyThread(id, replyId): let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id) - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(replyThreadMessageId.id)) |> map { result -> ResolveInternalUrlResult in switch result { @@ -866,7 +866,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId))) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId))) } else { return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -947,7 +947,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .single(.progress) |> then(foundPeer |> mapToSignal { foundPeer -> Signal in if let foundPeer = foundPeer { - if case let .channel(channel) = foundPeer, channel.flags.contains(.isForum) { + if case let .channel(channel) = foundPeer, channel.isForumOrMonoForum { if let threadId = threadId { return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(threadId)) |> map { result -> ResolveInternalUrlResult in @@ -956,7 +956,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -980,7 +980,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) }