From f47fa686e9c400f7000dbbafefacb2531330602a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 25 Jan 2022 14:08:52 +0400 Subject: [PATCH] Reactions update [skip ci] --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 + .../Postbox/Sources/MessageHistoryTable.swift | 4 +- .../MessageHistoryTagsSummaryTable.swift | 2 +- .../Sources/Account/Account.swift | 1 + .../Sources/Account/AccountManager.swift | 1 + .../State/AccountStateManagementUtils.swift | 33 ---- .../Sources/State/AccountViewTracker.swift | 23 +++ ...anagedConsumePersonalMessagesActions.swift | 91 +++++++---- ...kAllUnseenPersonalMessagesOperations.swift | 145 ++++++++++++++++- ...rkAllUnseenPersonalMessagesOperation.swift | 23 ++- .../SyncCore/SyncCore_Namespaces.swift | 1 + ...rkAllUnseenPersonalMessagesOperation.swift | 16 ++ .../ApplyMaxReadIndexInteractively.swift | 10 ++ .../TelegramUI/Sources/ChatController.swift | 153 ++++++++++++++++-- .../Sources/ChatHistoryListNode.swift | 4 +- .../ChatHistoryNavigationButtonNode.swift | 43 +++-- .../ChatHistoryNavigationButtons.swift | 63 ++------ ...essageContextControllerContentSource.swift | 34 ++++ 18 files changed, 497 insertions(+), 152 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index e67ef92237..d616c53d36 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7250,3 +7250,5 @@ Sorry for the inconvenience."; "Group.Members.Contacts" = "CONTACTS IN THIS GROUP"; "Group.Members.Other" = "OTHERS MEMBERS"; + +"Conversation.ReadAllReactions" = "Read All Reactions"; diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index b32c9f695f..152abf84b3 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -1481,12 +1481,12 @@ final class MessageHistoryTable: Table { let updatedGroupInfo = self.updateMovingGroupInfoInNamespace(index: updatedIndex, updatedIndex: updatedIndex, groupingKey: message.groupingKey, previousInfo: previousMessage.groupInfo, updatedGroupInfos: &updatedGroupInfos) if previousMessage.tags != message.tags || index != updatedIndex { - let isNewlyAdded = previousMessage.tags.isEmpty if !previousMessage.tags.isEmpty { self.tagsTable.remove(tags: previousMessage.tags, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } if !message.tags.isEmpty { - self.tagsTable.add(tags: message.tags, index: message.index, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + //let isNewlyAdded = previousMessage.tags.isEmpty + self.tagsTable.add(tags: message.tags, index: message.index, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } } if previousMessage.threadId != message.threadId || index != message.index { diff --git a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift index 3aedbe2057..8ae38715f2 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift @@ -133,7 +133,7 @@ class MessageHistoryTagsSummaryTable: Table { func removeMessage(key: MessageHistoryTagsSummaryKey, id: MessageId.Id, updatedSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) { if let current = self.get(key) { if current.count == 0 { - self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag), operations: &invalidateSummaries) + //self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag), operations: &invalidateSummaries) } else { self.set(key, summary: current.withAddedCount(-1), updatedSummaries: &updatedSummaries) } diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 53a1f1c04a..b60f7b1d69 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -1086,6 +1086,7 @@ public class Account { self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedReadReactionActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) + self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenReactionsOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedApplyPendingScheduledMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index b4b6e2d91f..117ca9e782 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -157,6 +157,7 @@ private var declaredEncodables: Void = { declareEncodable(TelegramDeviceContactImportedData.self, f: { TelegramDeviceContactImportedData(decoder: $0) }) declareEncodable(SecureFileMediaResource.self, f: { SecureFileMediaResource(decoder: $0) }) declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) }) + declareEncodable(SynchronizeMarkAllUnseenReactionsOperation.self, f: { SynchronizeMarkAllUnseenReactionsOperation(decoder: $0) }) declareEncodable(SynchronizeAppLogEventsOperation.self, f: { SynchronizeAppLogEventsOperation(decoder: $0) }) declareEncodable(TelegramMediaPoll.self, f: { TelegramMediaPoll(decoder: $0) }) declareEncodable(TelegramMediaUnsupported.self, f: { TelegramMediaUnsupported(decoder: $0) }) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 0dbaabe5df..e181c39161 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -3313,39 +3313,6 @@ func replayFinalState( attributes.append(updatedReactions) } - if let eventTimestamp = eventTimestamp, !currentMessage.flags.contains(.Incoming), let chatPeer = currentMessage.peers[currentMessage.id.peerId] { - let _ = chatPeer - - var previousCount = 0 - if let previousReactions = previousReactions { - for reaction in previousReactions.reactions { - previousCount += Int(reaction.count) - } - } - - var updatedCount = 0 - for reaction in updatedReactions.reactions { - updatedCount += Int(reaction.count) - } - - if updatedCount > previousCount { - if let topPeer = updatedReactions.recentPeers.last { - var wasPresentBefore = false - if let previousReactions = previousReactions { - for recentPeer in previousReactions.recentPeers { - if recentPeer.peerId == topPeer.peerId { - wasPresentBefore = true - break - } - } - } - if !wasPresentBefore, let reactionAuthor = transaction.getPeer(topPeer.peerId), transaction.isPeerContact(peerId: topPeer.peerId) { - generatedEvent = (reactionAuthor: reactionAuthor, message: currentMessage.withUpdatedAttributes(attributes), timestamp: eventTimestamp) - } - } - } - } - var tags = currentMessage.tags if updatedReactions.hasUnseen { tags.insert(.unseenReaction) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 81877b0763..c958e096b6 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1228,6 +1228,29 @@ public final class AccountViewTracker { } } + public func updateMarkAllReactionsSeen(peerId: PeerId) { + self.queue.async { + guard let account = self.account else { + return + } + let _ = (account.postbox.transaction { transaction -> Set in + let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id })) + if let summary = transaction.getMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud), summary.count > 0 { + var maxId: Int32 = summary.range.maxId + if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { + maxId = index.id.id + } + + transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) + addSynchronizeMarkAllUnseenReactionsOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) + } + + return ids + } + |> deliverOn(self.queue)).start() + } + } + public func updateMarkReactionsSeenForMessageIds(messageIds: Set) { self.queue.async { let addedMessageIds: [MessageId] = Array(messageIds) diff --git a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift index 5228ddd67e..b1d15e0d23 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift @@ -283,40 +283,71 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox } private func synchronizeReadMessageReactions(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, id: MessageId) -> Signal { - guard let inputPeer = transaction.getPeer(id.peerId).flatMap(apiInputPeer) else { - return .complete() - } - return network.request(Api.functions.messages.readReactions(peer: inputPeer)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal in - if let result = result { - switch result { - case let .affectedHistory(pts, ptsCount, _): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup { + return network.request(Api.functions.messages.readMessageContents(id: [id.id])) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + if let result = result { + switch result { + case let .affectedMessages(pts, ptsCount): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + } + } + return postbox.transaction { transaction -> Void in + transaction.setPendingMessageAction(type: .readReaction, id: id, action: nil) + transaction.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ReactionsMessageAttribute, attribute.hasUnseen { + attributes[j] = attribute.withAllSeen() + break loop + } + } + var updatedTags = currentMessage.tags + updatedTags.remove(.unseenReaction) + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) } } - return postbox.transaction { transaction -> Void in - transaction.setPendingMessageAction(type: .readReaction, id: id, action: nil) - transaction.updateMessage(id, update: { currentMessage in - var storeForwardInfo: StoreMessageForwardInfo? - if let forwardInfo = currentMessage.forwardInfo { - storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } else if id.peerId.namespace == Namespaces.Peer.CloudChannel { + if let peer = transaction.getPeer(id.peerId), let inputChannel = apiInputChannel(peer) { + return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: [id.id])) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Void in + transaction.setPendingMessageAction(type: .readReaction, id: id, action: nil) + transaction.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ReactionsMessageAttribute, attribute.hasUnseen { + attributes[j] = attribute.withAllSeen() + break loop + } + } + var updatedTags = currentMessage.tags + updatedTags.remove(.unseenReaction) + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) } - var attributes = currentMessage.attributes - loop: for j in 0 ..< attributes.count { - if let attribute = attributes[j] as? ReactionsMessageAttribute, attribute.hasUnseen { - attributes[j] = attribute.withAllSeen() - break loop - } - } - var updatedTags = currentMessage.tags - updatedTags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) - }) + } + } else { + return .complete() } + } else { + return .complete() } } diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift index 46dd09621c..c33ca8cd98 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift @@ -4,7 +4,6 @@ import Postbox import SwiftSignalKit import MtProtoKit - private final class ManagedSynchronizeMarkAllUnseenPersonalMessagesOperationsHelper { var operationDisposables: [Int32: Disposable] = [:] @@ -49,11 +48,11 @@ private final class ManagedSynchronizeMarkAllUnseenPersonalMessagesOperationsHel } } -private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal) -> Signal { +private func withTakenOperation(postbox: Postbox, peerId: PeerId, operationType: T.Type, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal) -> Signal { return postbox.transaction { transaction -> Signal in var result: PeerMergedOperationLogEntry? transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in - if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizeMarkAllUnseenPersonalMessagesOperation { + if let entry = entry, let _ = entry.mergedIndex, entry.contents is T { result = entry.mergedEntry! return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) } else { @@ -62,7 +61,8 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera }) return f(transaction, result) - } |> switchToLatest + } + |> switchToLatest } func managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal { @@ -81,7 +81,7 @@ func managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: Postbox, } for (entry, disposable) in beginOperations { - let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal in + let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, operationType: SynchronizeMarkAllUnseenPersonalMessagesOperation.self, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal in if let entry = entry { if let operation = entry.contents as? SynchronizeMarkAllUnseenPersonalMessagesOperation { return synchronizeMarkAllUnseen(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, peerId: entry.peerId, operation: operation) @@ -233,6 +233,141 @@ func markUnseenPersonalMessage(transaction: Transaction, id: MessageId, addSynch } } +func managedSynchronizeMarkAllUnseenReactionsOperations(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal { + return Signal { _ in + let tag: PeerOperationLogTag = OperationLogTags.SynchronizeMarkAllUnseenReactions + + let helper = Atomic(value: ManagedSynchronizeMarkAllUnseenPersonalMessagesOperationsHelper()) + + let disposable = postbox.mergedOperationLogView(tag: tag, limit: 10).start(next: { view in + let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in + return helper.update(view.entries) + } + + for disposable in disposeOperations { + disposable.dispose() + } + + for (entry, disposable) in beginOperations { + let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, operationType: SynchronizeMarkAllUnseenReactionsOperation.self, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal in + if let entry = entry { + if let operation = entry.contents as? SynchronizeMarkAllUnseenReactionsOperation { + return synchronizeMarkAllUnseenReactions(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, peerId: entry.peerId, operation: operation) + } else { + assertionFailure() + } + } + return .complete() + }) + |> then(postbox.transaction { transaction -> Void in + let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex) + }) + + disposable.set(signal.start()) + } + }) + + return ActionDisposable { + let disposables = helper.with { helper -> [Disposable] in + return helper.reset() + } + for disposable in disposables { + disposable.dispose() + } + disposable.dispose() + } + } +} + +private func synchronizeMarkAllUnseenReactions(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, operation: SynchronizeMarkAllUnseenReactionsOperation) -> Signal { + guard let inputPeer = transaction.getPeer(peerId).flatMap(apiInputPeer) else { + return .complete() + } + let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel) + let limit: Int32 = 100 + let oneOperation: (Int32) -> Signal = { maxId in + return network.request(Api.functions.messages.getUnreadReactions(peer: inputPeer, offsetId: maxId, addOffset: maxId == 0 ? 0 : -1, limit: limit, maxId: maxId == 0 ? 0 : (maxId + 1), minId: 1)) + |> mapToSignal { result -> Signal<[MessageId], MTRpcError> in + switch result { + case let .messages(messages, _, _): + return .single(messages.compactMap({ $0.id() })) + case let .channelMessages(_, _, _, _, messages, _, _): + return .single(messages.compactMap({ $0.id() })) + case .messagesNotModified: + return .single([]) + case let .messagesSlice(_, _, _, _, messages, _, _): + return .single(messages.compactMap({ $0.id() })) + } + } + |> mapToSignal { ids -> Signal in + let filteredIds = ids.filter { $0.id <= operation.maxId } + if filteredIds.isEmpty { + return .single(ids.min()?.id) + } + if peerId.namespace == Namespaces.Peer.CloudChannel { + guard let inputChannel = inputChannel else { + return .single(nil) + } + return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: filteredIds.map { $0.id })) + |> map { result -> Int32? in + if ids.count < limit { + return nil + } else { + return ids.min()?.id + } + } + } else { + return network.request(Api.functions.messages.readMessageContents(id: filteredIds.map { $0.id })) + |> map { result -> Int32? in + switch result { + case let .affectedMessages(pts, ptsCount): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + } + if ids.count < limit { + return nil + } else { + return ids.min()?.id + } + } + } + } + } + let currentMaxId = Atomic(value: 0) + let loopOperations: Signal = ( + ( + deferred { + return oneOperation(currentMaxId.with { $0 }) + } + |> `catch` { error -> Signal in + return .fail(.error(error)) + } + ) + |> mapToSignal { resultId -> Signal in + if let resultId = resultId { + let previous = currentMaxId.swap(resultId) + if previous == resultId { + return .fail(.done) + } else { + return .complete() + } + } else { + return .fail(.done) + } + } + |> `catch` { error -> Signal in + switch error { + case .done, .error: + return .fail(error) + } + } + |> restart + ) + return loopOperations + |> `catch` { _ -> Signal in + return .complete() + } +} + func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynchronizeAction: Bool) { if let message = transaction.getMessage(id) { var consume = false diff --git a/submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift index 9b962f5347..ec9b12729d 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift @@ -2,7 +2,6 @@ import Foundation import Postbox import SwiftSignalKit - func addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: Transaction, peerId: PeerId, maxId: MessageId.Id) { let tag: PeerOperationLogTag = OperationLogTags.SynchronizeMarkAllUnseenPersonalMessages @@ -25,3 +24,25 @@ func addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: Transacti transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeMarkAllUnseenPersonalMessagesOperation(maxId: maxId)) } + +func addSynchronizeMarkAllUnseenReactionsOperation(transaction: Transaction, peerId: PeerId, maxId: MessageId.Id) { + let tag: PeerOperationLogTag = OperationLogTags.SynchronizeMarkAllUnseenReactions + var topLocalIndex: Int32? + var currentMaxId: MessageId.Id? + transaction.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in + topLocalIndex = entry.tagLocalIndex + if let operation = entry.contents as? SynchronizeMarkAllUnseenReactionsOperation { + currentMaxId = operation.maxId + } + return false + }) + + if let topLocalIndex = topLocalIndex { + if let currentMaxId = currentMaxId, currentMaxId >= maxId { + return + } + let _ = transaction.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: topLocalIndex) + } + + transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeMarkAllUnseenReactionsOperation(maxId: maxId)) +} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index 8c69db3f22..2461c195d1 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -156,6 +156,7 @@ public struct OperationLogTags { public static let SynchronizeAppLogEvents = PeerOperationLogTag(value: 18) public static let SynchronizeEmojiKeywords = PeerOperationLogTag(value: 19) public static let SynchronizeChatListFilters = PeerOperationLogTag(value: 20) + public static let SynchronizeMarkAllUnseenReactions = PeerOperationLogTag(value: 21) } public struct LegacyPeerSummaryCounterTags: OptionSet, Sequence, Hashable { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeMarkAllUnseenPersonalMessagesOperation.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeMarkAllUnseenPersonalMessagesOperation.swift index bf8d597443..5f45fd0c6f 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeMarkAllUnseenPersonalMessagesOperation.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeMarkAllUnseenPersonalMessagesOperation.swift @@ -15,3 +15,19 @@ public final class SynchronizeMarkAllUnseenPersonalMessagesOperation: PostboxCod encoder.encodeInt32(self.maxId, forKey: "maxId") } } + +public final class SynchronizeMarkAllUnseenReactionsOperation: PostboxCoding { + public let maxId: MessageId.Id + + public init(maxId: MessageId.Id) { + self.maxId = maxId + } + + public init(decoder: PostboxDecoder) { + self.maxId = decoder.decodeInt32ForKey("maxId", orElse: Int32.min + 1) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.maxId, forKey: "maxId") + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index e7b41aedc2..80f02a38dd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -166,6 +166,16 @@ public func clearPeerUnseenPersonalMessagesInteractively(account: Account, peerI |> ignoreValues } +public func clearPeerUnseenReactionsInteractively(account: Account, peerId: PeerId) -> Signal { + return account.postbox.transaction { transaction -> Void in + if peerId.namespace == Namespaces.Peer.SecretChat { + return + } + account.viewTracker.updateMarkAllReactionsSeen(peerId: peerId) + } + |> ignoreValues +} + public func markAllChatsAsReadInteractively(transaction: Transaction, viewTracker: AccountViewTracker, groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?) { for peerId in transaction.getUnreadChatListPeerIds(groupId: groupId, filterPredicate: filterPredicate) { togglePeerUnreadMarkInteractively(transaction: transaction, viewTracker: viewTracker, peerId: peerId, setToValue: false) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3e241dabd7..b075d086b5 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5842,26 +5842,43 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - self.chatDisplayNode.navigateButtons.mentionsMenu = { [weak self] in + self.chatDisplayNode.navigateButtons.mentionsButton.activated = { [weak self] gesture, _ in guard let strongSelf = self else { + gesture.cancel() return } - let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.WebSearch_RecentSectionClear, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() + + strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + var menuItems: [ContextMenuItem] = [] + menuItems.append(.action(ContextMenuActionItem( + id: nil, + text: strongSelf.presentationData.strings.WebSearch_RecentSectionClear, + textColor: .primary, + textLayout: .singleLine, + icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Read"), color: theme.contextMenu.primaryColor) + }, + action: { _, f in + f(.dismissWithoutContent) + guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { return } let _ = clearPeerUnseenPersonalMessagesInteractively(account: strongSelf.context.account, peerId: peerId).start() - }) - ]), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(actionSheet, in: .window(.root)) + } + ))) + let items = ContextController.Items(content: .list(menuItems)) + + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.mentionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) + + strongSelf.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss() + } + return true + }) + strongSelf.window?.presentInGlobalOverlay(controller) } self.chatDisplayNode.navigateButtons.reactionsPressed = { [weak self] in @@ -5872,7 +5889,72 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch result { case let .result(messageId): if let messageId = messageId { - strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil)) + strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil), completion: { + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in + guard let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item else { + return + } + guard item.message.id == messageId else { + return + } + var maybeUpdatedReaction: String? + if let attribute = item.message.reactionsAttribute { + for recentPeer in attribute.recentPeers { + if recentPeer.isUnseen { + maybeUpdatedReaction = recentPeer.value + break + } + } + } + + guard let updatedReaction = maybeUpdatedReaction else { + return + } + + guard let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction) else { + return + } + for reaction in availableReactions.reactions { + guard let centerAnimation = reaction.centerAnimation else { + continue + } + guard let aroundAnimation = reaction.aroundAnimation else { + continue + } + + if reaction.value == updatedReaction { + let standaloneReactionAnimation = StandaloneReactionAnimation() + + strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation) + + strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation) + standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds + standaloneReactionAnimation.animateReactionSelection( + context: strongSelf.context, + theme: strongSelf.presentationData.theme, + reaction: ReactionContextItem( + reaction: ReactionContextItem.Reaction(rawValue: reaction.value), + appearAnimation: reaction.appearAnimation, + stillAnimation: reaction.selectAnimation, + listAnimation: centerAnimation, + largeListAnimation: reaction.activateAnimation, + applicationAnimation: aroundAnimation, + largeApplicationAnimation: reaction.effectAnimation + ), + targetView: targetView, + completion: { [weak standaloneReactionAnimation] in + standaloneReactionAnimation?.removeFromSupernode() + } + ) + + break + } + } + } + }) } case .loading: break @@ -5882,6 +5964,45 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } + self.chatDisplayNode.navigateButtons.reactionsButton.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + gesture.cancel() + return + } + + strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + var menuItems: [ContextMenuItem] = [] + menuItems.append(.action(ContextMenuActionItem( + id: nil, + text: strongSelf.presentationData.strings.Conversation_ReadAllReactions, + textColor: .primary, + textLayout: .singleLine, + icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Read"), color: theme.contextMenu.primaryColor) + }, + action: { _, f in + f(.dismissWithoutContent) + + guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { + return + } + let _ = clearPeerUnseenReactionsInteractively(account: strongSelf.context.account, peerId: peerId).start() + } + ))) + let items = ContextController.Items(content: .list(menuItems)) + + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.reactionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) + + strongSelf.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss() + } + return true + }) + strongSelf.window?.presentInGlobalOverlay(controller) + } + let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, completion in guard let strongSelf = self, strongSelf.isNodeLoaded else { return @@ -12385,7 +12506,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.loadingMessage.set(.single(nil)) self.messageIndexDisposable.set(nil) self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: message.index, animated: animated, scrollPosition: scrollPosition) - completion?() + Queue.mainQueue().after(0.25, { + completion?() + }) if case let .id(_, maybeTimecode) = messageLocation, let timecode = maybeTimecode { let _ = self.controllerInteraction?.openMessage(message, .timecode(timecode)) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index c97220caeb..6dfa244e02 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -732,7 +732,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { guard let strongSelf = self else { return } - if strongSelf.canReadHistoryValue { + if strongSelf.canReadHistoryValue && !strongSelf.context.sharedContext.immediateExperimentalUISettings.skipReadHistory { strongSelf.context.account.viewTracker.updateMarkReactionsSeenForMessageIds(messageIds: messageIds) } else { strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.formUnion(messageIds) @@ -1342,7 +1342,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds) } - if strongSelf.canReadHistoryValue && !strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.isEmpty { + if strongSelf.canReadHistoryValue && !strongSelf.context.sharedContext.immediateExperimentalUISettings.skipReadHistory && !strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.isEmpty { let messageIds = strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.removeAll() context?.account.viewTracker.updateMarkReactionsSeenForMessageIds(messageIds: messageIds) diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift index 015866b4aa..30dd37cf30 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift @@ -12,7 +12,9 @@ enum ChatHistoryNavigationButtonType { case reactions } -class ChatHistoryNavigationButtonNode: ASControlNode { +class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { + let containerNode: ContextExtractedContentContainingNode + private let buttonNode: HighlightTrackingButtonNode private let backgroundNode: NavigationBackgroundNode private let imageNode: ASImageNode private let badgeBackgroundNode: ASImageNode @@ -22,9 +24,9 @@ class ChatHistoryNavigationButtonNode: ASControlNode { didSet { if (oldValue != nil) != (self.tapped != nil) { if self.tapped != nil { - self.addTarget(self, action: #selector(onTap), forControlEvents: .touchUpInside) + self.buttonNode.addTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside) } else { - self.removeTarget(self, action: #selector(onTap), forControlEvents: .touchUpInside) + self.buttonNode.removeTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside) } } } @@ -44,6 +46,9 @@ class ChatHistoryNavigationButtonNode: ASControlNode { init(theme: PresentationTheme, type: ChatHistoryNavigationButtonType) { self.theme = theme self.type = type + + self.containerNode = ContextExtractedContentContainingNode() + self.buttonNode = HighlightTrackingButtonNode() self.backgroundNode = NavigationBackgroundNode(color: theme.chat.inputPanel.panelBackgroundColor) @@ -71,18 +76,30 @@ class ChatHistoryNavigationButtonNode: ASControlNode { self.badgeTextNode.displaysAsynchronously = false super.init() - - self.addSubnode(self.backgroundNode) - self.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: 38.0 / 2.0, transition: .immediate) - - self.addSubnode(self.imageNode) - self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)) - self.addSubnode(self.badgeBackgroundNode) - self.addSubnode(self.badgeTextNode) + self.targetNodeForActivationProgress = self.buttonNode - self.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)) + self.addSubnode(self.containerNode) + + let size = CGSize(width: 38.0, height: 38.0) + + self.containerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) + self.containerNode.contentRect = CGRect(origin: CGPoint(), size: size) + + self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) + self.containerNode.contentNode.addSubnode(self.buttonNode) + + self.buttonNode.addSubnode(self.backgroundNode) + self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size) + self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: size.width / 2.0, transition: .immediate) + + self.buttonNode.addSubnode(self.imageNode) + self.imageNode.frame = CGRect(origin: CGPoint(), size: size) + + self.buttonNode.addSubnode(self.badgeBackgroundNode) + self.buttonNode.addSubnode(self.badgeTextNode) + + self.frame = CGRect(origin: CGPoint(), size: size) } func updateTheme(theme: PresentationTheme) { diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift index ad87c59d1e..633ca9810e 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift @@ -8,10 +8,8 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { private var theme: PresentationTheme private var dateTimeFormat: PresentationDateTimeFormat - private let reactionsButton: ChatHistoryNavigationButtonNode - private let reactionsButtonTapNode: ASDisplayNode - private let mentionsButton: ChatHistoryNavigationButtonNode - private let mentionsButtonTapNode: ASDisplayNode + let reactionsButton: ChatHistoryNavigationButtonNode + let mentionsButton: ChatHistoryNavigationButtonNode private let downButton: ChatHistoryNavigationButtonNode var downPressed: (() -> Void)? { @@ -21,9 +19,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { } var reactionsPressed: (() -> Void)? - var reactionsMenu: (() -> Void)? var mentionsPressed: (() -> Void)? - var mentionsMenu: (() -> Void)? var displayDownButton: Bool = false { didSet { @@ -78,12 +74,10 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { self.mentionsButton = ChatHistoryNavigationButtonNode(theme: theme, type: .mentions) self.mentionsButton.alpha = 0.0 self.mentionsButton.isHidden = true - self.mentionsButtonTapNode = ASDisplayNode() self.reactionsButton = ChatHistoryNavigationButtonNode(theme: theme, type: .reactions) self.reactionsButton.alpha = 0.0 self.reactionsButton.isHidden = true - self.reactionsButtonTapNode = ASDisplayNode() self.downButton = ChatHistoryNavigationButtonNode(theme: theme, type: .down) self.downButton.alpha = 0.0 @@ -91,29 +85,23 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { super.init() - self.mentionsButton.isUserInteractionEnabled = false - self.addSubnode(self.reactionsButton) - self.addSubnode(self.reactionsButtonTapNode) self.addSubnode(self.mentionsButton) - self.addSubnode(self.mentionsButtonTapNode) self.addSubnode(self.downButton) + + self.reactionsButton.tapped = { [weak self] in + self?.reactionsPressed?() + } + + self.mentionsButton.tapped = { [weak self] in + self?.mentionsPressed?() + } + + self.downButton.isGestureEnabled = false } override func didLoad() { super.didLoad() - - let reactionsTapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.reactionsTap(_:))) - reactionsTapRecognizer.tapActionAtPoint = { _ in - return .waitForSingleTap - } - self.reactionsButtonTapNode.view.addGestureRecognizer(reactionsTapRecognizer) - - let mentionsTapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.mentionsTap(_:))) - mentionsTapRecognizer.tapActionAtPoint = { _ in - return .waitForSingleTap - } - self.mentionsButtonTapNode.view.addGestureRecognizer(mentionsTapRecognizer) } func update(theme: PresentationTheme, dateTimeFormat: PresentationDateTimeFormat) { @@ -121,6 +109,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { self.theme = theme self.dateTimeFormat = dateTimeFormat + self.reactionsButton.updateTheme(theme: theme) self.mentionsButton.updateTheme(theme: theme) self.downButton.updateTheme(theme: theme) } @@ -154,7 +143,6 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { self.mentionsButton.isHidden = false transition.updateAlpha(node: self.mentionsButton, alpha: 1.0) transition.updateTransformScale(node: self.mentionsButton, scale: 1.0) - self.mentionsButtonTapNode.isHidden = false } else { transition.updateAlpha(node: self.mentionsButton, alpha: 0.0, completion: { [weak self] completed in guard let strongSelf = self, completed else { @@ -163,14 +151,12 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { strongSelf.mentionsButton.isHidden = true }) transition.updateTransformScale(node: self.mentionsButton, scale: 0.2) - self.mentionsButtonTapNode.isHidden = true } if self.reactionsCount != 0 { self.reactionsButton.isHidden = false transition.updateAlpha(node: self.reactionsButton, alpha: 1.0) transition.updateTransformScale(node: self.reactionsButton, scale: 1.0) - self.reactionsButtonTapNode.isHidden = false } else { transition.updateAlpha(node: self.reactionsButton, alpha: 0.0, completion: { [weak self] completed in guard let strongSelf = self, completed else { @@ -179,16 +165,13 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { strongSelf.reactionsButton.isHidden = true }) transition.updateTransformScale(node: self.reactionsButton, scale: 0.2) - self.reactionsButtonTapNode.isHidden = true } transition.updatePosition(node: self.downButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height), size: buttonSize).center) transition.updatePosition(node: self.mentionsButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize).center) - self.mentionsButtonTapNode.frame = CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize) transition.updatePosition(node: self.reactionsButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset - reactionsOffset), size: buttonSize).center) - self.reactionsButtonTapNode.frame = CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize) return completeSize } @@ -206,26 +189,6 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { } return nil } - - @objc private func reactionsTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { - if case .ended = recognizer.state, let gesture = recognizer.lastRecognizedGestureAndLocation?.0 { - if case .tap = gesture { - self.reactionsPressed?() - } else if case .longTap = gesture { - self.reactionsMenu?() - } - } - } - - @objc private func mentionsTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { - if case .ended = recognizer.state, let gesture = recognizer.lastRecognizedGestureAndLocation?.0 { - if case .tap = gesture { - self.mentionsPressed?() - } else if case .longTap = gesture { - self.mentionsMenu?() - } - } - } final class SnapshotState { fileprivate let downButtonSnapshotView: UIView? diff --git a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift index 8a37064a6c..4ef983e99f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift @@ -160,3 +160,37 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo } } +final class ChatMessageNavigationButtonContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = false + let ignoreContentTouches: Bool = true + let blurBackground: Bool = true + let centerActionsHorizontally: Bool = true + + private weak var chatNode: ChatControllerNode? + private let contentNode: ContextExtractedContentContainingNode + + var shouldBeDismissed: Signal { + return .single(false) + } + + init(chatNode: ChatControllerNode, contentNode: ContextExtractedContentContainingNode) { + self.chatNode = chatNode + self.contentNode = contentNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + guard let chatNode = self.chatNode else { + return nil + } + + return ContextControllerTakeViewInfo(contentContainingNode: self.contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil)) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + guard let chatNode = self.chatNode else { + return nil + } + + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil)) + } +}