diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index c77f88a17d..2980f5ad69 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -3716,7 +3716,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController func deletePeerThread(peerId: EnginePeer.Id, threadId: Int64) { let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - + //TODO:localize items.append(ActionSheetTextItem(title: "This will delete the topic with all its messages", parseMarkdown: true)) items.append(ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index ca5add8f93..873e742f05 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1267,7 +1267,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, } #if DEBUG - //debugSaveState(basePath: basePath, name: "previous1") + debugSaveState(basePath: basePath, name: "previous1") //debugRestoreState(basePath: basePath, name: "previous1") #endif diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index 0f1d35fb49..44894d7877 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -1328,6 +1328,49 @@ public final class AccountStateManager { } } +func resolveNotificationSettings(list: [TelegramPeerNotificationSettings], defaultSettings: MessageNotificationSettings) -> (sound: PeerMessageSound, notify: Bool, displayContents: Bool) { + var sound: PeerMessageSound = defaultSettings.sound + + var notify = defaultSettings.enabled + var displayContents = defaultSettings.displayPreviews + + for item in list.reversed() { + if case .default = item.messageSound { + } else { + sound = item.messageSound + } + + switch item.displayPreviews { + case .default: + break + case .show: + displayContents = true + case .hide: + displayContents = false + } + + switch item.muteState { + case .default: + break + case .unmuted: + notify = true + case let .muted(deadline): + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + if deadline > timestamp { + notify = false + } else { + notify = true + } + } + } + + if case .default = sound { + sound = defaultCloudPeerNotificationSound + } + + return (sound, notify, displayContents) +} + public func messagesForNotification(transaction: Transaction, id: MessageId, alwaysReturnMessage: Bool) -> (messages: [Message], notify: Bool, sound: PeerMessageSound, displayContents: Bool, threadData: MessageHistoryThreadData?) { guard let message = transaction.getMessage(id) else { Logger.shared.log("AccountStateManager", "notification message doesn't exist") @@ -1335,7 +1378,6 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw } var notify = true - var sound: PeerMessageSound = defaultCloudPeerNotificationSound var muted = false var displayContents = true var threadData: MessageHistoryThreadData? @@ -1365,8 +1407,6 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw } } - let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - var notificationPeerId = id.peerId let peer = transaction.getPeer(id.peerId) if let peer = peer, let associatedPeerId = peer.associatedPeerId { @@ -1376,47 +1416,38 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw notificationPeerId = author.id } + var notificationSettingsStack: [TelegramPeerNotificationSettings] = [] + + if let threadId = message.threadId, let threadData = transaction.getMessageHistoryThreadInfo(peerId: message.id.peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { + notificationSettingsStack.append(threadData.notificationSettings) + } + if let notificationSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings { - var defaultSound: PeerMessageSound = defaultCloudPeerNotificationSound - var defaultNotify: Bool = true - if let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) { - if id.peerId.namespace == Namespaces.Peer.CloudUser { - defaultNotify = globalNotificationSettings.effective.privateChats.enabled - defaultSound = globalNotificationSettings.effective.privateChats.sound - displayContents = globalNotificationSettings.effective.privateChats.displayPreviews - } else if id.peerId.namespace == Namespaces.Peer.SecretChat { - defaultNotify = globalNotificationSettings.effective.privateChats.enabled - defaultSound = globalNotificationSettings.effective.privateChats.sound - displayContents = false - } else if id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = peer as? TelegramChannel, case .broadcast = peer.info { - defaultNotify = globalNotificationSettings.effective.channels.enabled - defaultSound = globalNotificationSettings.effective.channels.sound - displayContents = globalNotificationSettings.effective.channels.displayPreviews - } else { - defaultNotify = globalNotificationSettings.effective.groupChats.enabled - defaultSound = globalNotificationSettings.effective.groupChats.sound - displayContents = globalNotificationSettings.effective.groupChats.displayPreviews - } - } - switch notificationSettings.muteState { - case .default: - if !defaultNotify { - notify = false - } - case let .muted(until): - if until >= timestamp { - notify = false - } - case .unmuted: - break - } - if case .default = notificationSettings.messageSound { - sound = defaultSound - } else { - sound = notificationSettings.messageSound - } + notificationSettingsStack.append(notificationSettings) + } + + let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) ?? GlobalNotificationSettings.defaultSettings + + let defaultNotificationSettings: MessageNotificationSettings + if id.peerId.namespace == Namespaces.Peer.CloudUser { + defaultNotificationSettings = globalNotificationSettings.effective.privateChats + } else if id.peerId.namespace == Namespaces.Peer.SecretChat { + defaultNotificationSettings = globalNotificationSettings.effective.privateChats + displayContents = false + } else if id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = peer as? TelegramChannel, case .broadcast = peer.info { + defaultNotificationSettings = globalNotificationSettings.effective.channels } else { - Logger.shared.log("AccountStateManager", "notification settings for \(notificationPeerId) are undefined") + defaultNotificationSettings = globalNotificationSettings.effective.groupChats + } + + let (resolvedSound, resolvedNotify, resolvedDisplayContents) = resolveNotificationSettings(list: notificationSettingsStack, defaultSettings: defaultNotificationSettings) + + var sound = resolvedSound + if !resolvedNotify { + notify = false + } + if !resolvedDisplayContents { + displayContents = false } if muted { @@ -1425,10 +1456,10 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw if let channel = message.peers[message.id.peerId] as? TelegramChannel { switch channel.participationStatus { - case .kicked, .left: - return ([], false, sound, false, threadData) - case .member: - break + case .kicked, .left: + return ([], false, sound, false, threadData) + case .member: + break } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 96b3f5321a..e81997b2ab 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -270,14 +270,17 @@ private class AdMessagesHistoryContextImpl { struct CachedState: Codable, PostboxCoding { enum CodingKeys: String, CodingKey { case timestamp + case interPostInterval case messages } var timestamp: Int32 + var interPostInterval: Int32? var messages: [CachedMessage] - init(timestamp: Int32, messages: [CachedMessage]) { + init(timestamp: Int32, interPostInterval: Int32?, messages: [CachedMessage]) { self.timestamp = timestamp + self.interPostInterval = interPostInterval self.messages = messages } @@ -285,6 +288,7 @@ private class AdMessagesHistoryContextImpl { let container = try decoder.container(keyedBy: CodingKeys.self) self.timestamp = try container.decode(Int32.self, forKey: .timestamp) + self.interPostInterval = try container.decodeIfPresent(Int32.self, forKey: .interPostInterval) self.messages = try container.decode([CachedMessage].self, forKey: .messages) } @@ -292,11 +296,13 @@ private class AdMessagesHistoryContextImpl { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.timestamp, forKey: .timestamp) + try container.encodeIfPresent(self.interPostInterval, forKey: .interPostInterval) try container.encode(self.messages, forKey: .messages) } init(decoder: PostboxDecoder) { self.timestamp = decoder.decodeInt32ForKey("timestamp", orElse: 0) + self.interPostInterval = decoder.decodeOptionalInt32ForKey("interPostInterval") if let messagesData = decoder.decodeOptionalDataArrayForKey("messages") { self.messages = messagesData.compactMap { data -> CachedMessage? in return try? AdaptedPostboxDecoder().decode(CachedMessage.self, from: data) @@ -308,6 +314,11 @@ private class AdMessagesHistoryContextImpl { func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.timestamp, forKey: "timestamp") + if let interPostInterval = self.interPostInterval { + encoder.encodeInt32(interPostInterval, forKey: "interPostInterval") + } else { + encoder.encodeNil(forKey: "interPostInterval") + } encoder.encodeDataArray(self.messages.compactMap { message -> Data? in return try? AdaptedPostboxEncoder().encode(message) }, forKey: "messages") @@ -338,9 +349,13 @@ private class AdMessagesHistoryContextImpl { } struct State: Equatable { + var interPostInterval: Int32? var messages: [Message] static func ==(lhs: State, rhs: State) -> Bool { + if lhs.interPostInterval != rhs.interPostInterval { + return false + } if lhs.messages.count != rhs.messages.count { return false } @@ -372,43 +387,41 @@ private class AdMessagesHistoryContextImpl { self.account = account self.peerId = peerId - self.stateValue = State(messages: []) + self.stateValue = State(interPostInterval: nil, messages: []) self.state.set(CachedState.getCached(postbox: account.postbox, peerId: peerId) |> mapToSignal { cachedState -> Signal in if let cachedState = cachedState, cachedState.timestamp >= Int32(Date().timeIntervalSince1970) - 5 * 60 { return account.postbox.transaction { transaction -> State in - return State(messages: cachedState.messages.compactMap { message -> Message? in + return State(interPostInterval: cachedState.interPostInterval, messages: cachedState.messages.compactMap { message -> Message? in return message.toMessage(peerId: peerId, transaction: transaction) }) } } else { - return .single(State(messages: [])) + return .single(State(interPostInterval: nil, messages: [])) } }) - let signal: Signal<[Message], NoError> = account.postbox.transaction { transaction -> Api.InputChannel? in + let signal: Signal<(interPostInterval: Int32?, messages: [Message]), NoError> = account.postbox.transaction { transaction -> Api.InputChannel? in return transaction.getPeer(peerId).flatMap(apiInputChannel) } - |> mapToSignal { inputChannel -> Signal<[Message], NoError> in + |> mapToSignal { inputChannel -> Signal<(interPostInterval: Int32?, messages: [Message]), NoError> in guard let inputChannel = inputChannel else { - return .single([]) + return .single((nil, [])) } return account.network.request(Api.functions.channels.getSponsoredMessages(channel: inputChannel)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result -> Signal<[Message], NoError> in + |> mapToSignal { result -> Signal<(interPostInterval: Int32?, messages: [Message]), NoError> in guard let result = result else { - return .single([]) + return .single((nil, [])) } - return account.postbox.transaction { transaction -> [Message] in + return account.postbox.transaction { transaction -> (interPostInterval: Int32?, messages: [Message]) in switch result { case let .sponsoredMessages(_, postsBetween, messages, chats, users): - let _ = postsBetween - var peers: [Peer] = [] var peerPresences: [PeerId: Api.User] = [:] @@ -501,24 +514,24 @@ private class AdMessagesHistoryContextImpl { } } - CachedState.setCached(transaction: transaction, peerId: peerId, state: CachedState(timestamp: Int32(Date().timeIntervalSince1970), messages: parsedMessages)) + CachedState.setCached(transaction: transaction, peerId: peerId, state: CachedState(timestamp: Int32(Date().timeIntervalSince1970), interPostInterval: postsBetween, messages: parsedMessages)) - return parsedMessages.compactMap { message -> Message? in + return (postsBetween, parsedMessages.compactMap { message -> Message? in return message.toMessage(peerId: peerId, transaction: transaction) - } + }) case .sponsoredMessagesEmpty: - return [] + return (nil, []) } } } } self.disposable.set((signal - |> deliverOn(self.queue)).start(next: { [weak self] messages in + |> deliverOn(self.queue)).start(next: { [weak self] interPostInterval, messages in guard let strongSelf = self else { return } - strongSelf.stateValue = State(messages: messages) + strongSelf.stateValue = State(interPostInterval: interPostInterval, messages: messages) })) } @@ -549,13 +562,13 @@ public class AdMessagesHistoryContext { private let queue = Queue() private let impl: QueueLocalObject - public var state: Signal<[Message], NoError> { + public var state: Signal<(interPostInterval: Int32?, messages: [Message]), NoError> { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in let stateDisposable = impl.state.get().start(next: { state in - subscriber.putNext(state.messages) + subscriber.putNext((state.interPostInterval, state.messages)) }) disposable.set(stateDisposable) } diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 2e5c8cd99e..6e22927650 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -324,7 +324,7 @@ final class AuthorizedApplicationContext { var chatIsVisible = false if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.traceVisibility() { - if topController.chatLocation.peerId == firstMessage.id.peerId { + if topController.chatLocation.peerId == firstMessage.id.peerId, (topController.chatLocation.threadId == nil || topController.chatLocation.threadId == firstMessage.threadId) { chatIsVisible = true } } @@ -335,7 +335,7 @@ final class AuthorizedApplicationContext { if !chatIsVisible { strongSelf.mainWindow.forEachViewController({ controller in - if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, controller.chatLocation.threadId == chatLocation.threadId { + if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, (chatLocation.threadId == nil || chatLocation.threadId == controller.chatLocation.threadId) { chatIsVisible = true return false } @@ -415,14 +415,14 @@ final class AuthorizedApplicationContext { return true } - if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.chatLocation.peerId == chatLocation.peerId, topController.chatLocation.threadId == chatLocation.threadId { + if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.chatLocation.peerId == chatLocation.peerId, (topController.chatLocation.threadId == nil || topController.chatLocation.threadId == chatLocation.threadId) { strongSelf.notificationController.removeItemsWithGroupingKey(firstMessage.id.peerId) return false } for controller in strongSelf.rootController.viewControllers { - if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, controller.chatLocation.threadId == chatLocation.threadId { + if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, (controller.chatLocation.threadId == nil || controller.chatLocation.threadId == chatLocation.threadId) { return true } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index b29022e88a..02a282e145 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5565,9 +5565,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func topPinnedMessageSignal(latest: Bool) -> Signal { - let topPinnedMessage: Signal + var pinnedPeerId: EnginePeer.Id? + let threadId = self.chatLocation.threadId + switch self.chatLocation { - case let .peer(peerId): + case let .peer(id): + pinnedPeerId = id + case let .replyThread(message): + if message.isForumPost { + pinnedPeerId = self.chatLocation.peerId + } + default: + break + } + + if let peerId = pinnedPeerId { + let topPinnedMessage: Signal + struct ReferenceMessage { var id: MessageId var isScrolled: Bool @@ -5611,7 +5625,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G location = .Initial(count: count) } - return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: .peer(id: peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) + let chatLocation: ChatLocation + if let threadId { + chatLocation = .replyThread(message: ChatReplyThreadMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + } else { + chatLocation = .peer(id: peerId) + } + + return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: chatLocation, chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -5831,10 +5852,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return message } |> distinctUntilChanged - case .replyThread, .feed: + + return topPinnedMessage + } else { return .single(nil) } - return topPinnedMessage } override public func loadDisplayNode() { @@ -6241,7 +6263,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch strongSelf.chatLocation { case let .replyThread(replyThreadMessage): if isForum { - pinnedMessageId = nil + pinnedMessageId = topPinnedMessage?.message.id + pinnedMessage = topPinnedMessage } else { if isTopReplyThreadMessageShown { pinnedMessageId = nil @@ -8325,7 +8348,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, unblockPeer: { [weak self] in self?.unblockPeer() }, pinMessage: { [weak self] messageId, contextController in - if let strongSelf = self, case let .peer(currentPeerId) = strongSelf.chatLocation { + if let strongSelf = self, let currentPeerId = strongSelf.chatLocation.peerId { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { if strongSelf.canManagePin() { let pinAction: (Bool, Bool) -> Void = { notify, forThisPeerOnlyIfPossible in diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 95340c5df2..e91b449515 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1052,13 +1052,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var extraTransition = transition if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) { if self.titleAccessoryPanelNode != titleAccessoryPanelNode { - dismissedTitleAccessoryPanelNode = self.titleAccessoryPanelNode + dismissedTitleAccessoryPanelNode = self.titleAccessoryPanelNode self.titleAccessoryPanelNode = titleAccessoryPanelNode immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = true self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode) titleAccessoryPanelNode.clipsToBounds = true - extraTransition = .animated(duration: 0.2, curve: .easeInOut) + if transition.isAnimated { + extraTransition = .animated(duration: 0.2, curve: .easeInOut) + } } let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 39b48591c4..2df95d5687 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -25,7 +25,7 @@ func chatHistoryEntriesForView( customChannelDiscussionReadState: MessageId?, customThreadOutgoingReadState: MessageId?, cachedData: CachedPeerData?, - adMessages: [Message] + adMessages: (interPostInterval: Int32?, messages: [Message]) ) -> [ChatHistoryEntry] { if historyAppearsCleared { return [] @@ -329,9 +329,9 @@ func chatHistoryEntriesForView( } if view.laterId == nil && !view.isLoading { - if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], !adMessages.isEmpty { + if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], !adMessages.messages.isEmpty { var nextAdMessageId: Int32 = 1 - for message in adMessages { + if let message = adMessages.messages.first { let updatedMessage = Message( stableId: UInt32.max - 1 - UInt32(nextAdMessageId), stableVersion: message.stableVersion, diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index ab786bd484..80baeac4ad 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -638,14 +638,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { default: break } - var adMessages: Signal<[Message], NoError> + var adMessages: Signal<(interPostInterval: Int32?, messages: [Message]), NoError> if case .bubbles = mode, let peerId = displayAdPeer { let adMessagesContext = context.engine.messages.adMessages(peerId: peerId) self.adMessagesContext = adMessagesContext adMessages = adMessagesContext.state } else { self.adMessagesContext = nil - adMessages = .single([]) + adMessages = .single((nil, [])) } /*if case .bubbles = mode, let peerId = sparseScrollPeerId { @@ -661,7 +661,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { super.init() adMessages = adMessages - |> afterNext { [weak self] messages in + |> afterNext { [weak self] interPostInterval, messages in Queue.mainQueue().async { guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 74c05be355..41e3dd26b6 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1250,7 +1250,17 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } } - if data.canPin && !isMigrated, case .peer = chatPresentationInterfaceState.chatLocation { + var canPin = data.canPin + if case let .replyThread(message) = chatPresentationInterfaceState.chatLocation { + if !message.isForumPost { + canPin = false + } + } + if isMigrated { + canPin = false + } + + if canPin { var pinnedSelectedMessageId: MessageId? for message in messages { if message.tags.contains(.pinned) {