From 10147b56ca8cfaa9b375609f5456d5f8913b921a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 18:21:13 +0400 Subject: [PATCH] Fix shadow layout --- .../ReactionListContextMenuContent.swift | 9 +++- .../ReactionContextBackgroundNode.swift | 22 ++++++--- .../Account/AccountIntermediateState.swift | 16 ++++--- .../State/AccountStateManagementUtils.swift | 46 +++++++++++++++++-- .../Sources/State/AccountStateManager.swift | 18 ++++++++ .../Sources/State/MessageReactions.swift | 22 ++++++--- 6 files changed, 109 insertions(+), 24 deletions(-) diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index 0ae1d4ae53..a486a03aa4 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -398,9 +398,14 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent if !self.listState.canLoadMore { return self.mergedItems.count } else { - var value = self.listState.totalCount + let reactionCount = self.listState.totalCount + var value = reactionCount if let readStats = self.readStats { - value = max(value, readStats.peers.count) + if reactionCount < readStats.peers.count && self.listState.hasOutgoingReaction { + value = readStats.peers.count + 1 + } else { + value = max(reactionCount, readStats.peers.count) + } } return value } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift index 5a87fe2493..515cff7fe1 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift @@ -174,29 +174,37 @@ final class ReactionContextBackgroundNode: ASDisplayNode { self.largeCircleLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: largeCircleDelay) self.largeCircleLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) + self.largeCircleShadowLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) //self.largeCircleLayer.animate(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: largeCircleDuration) self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: mainCircleDelay) self.backgroundLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) + self.backgroundShadowLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) } func animateInFromAnchorRect(size: CGSize, sourceBackgroundFrame: CGRect) { let springDuration: Double = 0.2 let springDamping: CGFloat = 104.0 let springDelay: Double = 0.25 + let shadowInset: CGFloat = 15.0 - self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) - self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) - self.backgroundShadowLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) - self.backgroundShadowLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) + let contentBounds = self.backgroundNode.frame + + let visualSourceBackgroundFrame = sourceBackgroundFrame.offsetBy(dx: -contentBounds.minX, dy: -contentBounds.minY) + let sourceShadowFrame = visualSourceBackgroundFrame.insetBy(dx: -shadowInset, dy: -shadowInset) + + self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: visualSourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) + self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: visualSourceBackgroundFrame.size)), to: NSValue(cgRect: self.backgroundLayer.bounds), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) + self.backgroundShadowLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceShadowFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) + self.backgroundShadowLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceShadowFrame.size)), to: NSValue(cgRect: self.backgroundShadowLayer.bounds), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) } func animateOut() { self.backgroundLayer.animateAlpha(from: CGFloat(self.backgroundLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.backgroundShadowLayer.animateAlpha(from: CGFloat(self.backgroundShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.backgroundShadowLayer.animateAlpha(from: CGFloat(self.backgroundShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) self.largeCircleLayer.animateAlpha(from: CGFloat(self.largeCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.largeCircleShadowLayer.animateAlpha(from: CGFloat(self.largeCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.largeCircleShadowLayer.animateAlpha(from: CGFloat(self.largeCircleShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) self.smallCircleLayer.animateAlpha(from: CGFloat(self.smallCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.smallCircleShadowLayer.animateAlpha(from: CGFloat(self.smallCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.smallCircleShadowLayer.animateAlpha(from: CGFloat(self.smallCircleShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) } } diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 478dd24bf5..c27c3e73e0 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -65,7 +65,7 @@ enum AccountStateMutationOperation { case DeleteMessages([MessageId]) case EditMessage(MessageId, StoreMessage) case UpdateMessagePoll(MediaId, Api.Poll?, Api.PollResults) - case UpdateMessageReactions(MessageId, Api.MessageReactions) + case UpdateMessageReactions(MessageId, Api.MessageReactions, Int32?) case UpdateMedia(MediaId, Media?) case ReadInbox(MessageId) case ReadOutbox(MessageId, Int32?) @@ -258,8 +258,8 @@ struct AccountMutableState { self.addOperation(.UpdateMessagePoll(id, poll, results)) } - mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions) { - self.addOperation(.UpdateMessageReactions(messageId, reactions)) + mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions, eventTimestamp: Int32?) { + self.addOperation(.UpdateMessageReactions(messageId, reactions, eventTimestamp)) } mutating func updateMedia(_ id: MediaId, media: Media?) { @@ -610,6 +610,7 @@ struct AccountFinalState { struct AccountReplayedFinalState { let state: AccountFinalState let addedIncomingMessageIds: [MessageId] + let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] let wasScheduledMessageIds: [MessageId] let addedSecretMessageIds: [MessageId] let deletedMessageIds: [DeletedMessageId] @@ -627,6 +628,7 @@ struct AccountReplayedFinalState { struct AccountFinalStateEvents { let addedIncomingMessageIds: [MessageId] + let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] let wasScheduledMessageIds:[MessageId] let deletedMessageIds: [DeletedMessageId] let updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] @@ -646,11 +648,12 @@ struct AccountFinalStateEvents { let updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty + return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty } - init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { + init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { self.addedIncomingMessageIds = addedIncomingMessageIds + self.addedReactionEvents = addedReactionEvents self.wasScheduledMessageIds = wasScheduledMessageIds self.deletedMessageIds = deletedMessageIds self.updatedTypingActivities = updatedTypingActivities @@ -672,6 +675,7 @@ struct AccountFinalStateEvents { init(state: AccountReplayedFinalState) { self.addedIncomingMessageIds = state.addedIncomingMessageIds + self.addedReactionEvents = state.addedReactionEvents self.wasScheduledMessageIds = state.wasScheduledMessageIds self.deletedMessageIds = state.deletedMessageIds self.updatedTypingActivities = state.updatedTypingActivities @@ -714,6 +718,6 @@ struct AccountFinalStateEvents { let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId) let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated - return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs })) + return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs })) } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 67d2ccfa5a..4efc5b7060 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1474,7 +1474,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } }) case let .updateMessageReactions(peer, msgId, reactions): - updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions) + updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions, eventTimestamp: updatesDate) default: break } @@ -2430,6 +2430,7 @@ func replayFinalState( } var wasScheduledMessageIds:[MessageId] = [] var addedIncomingMessageIds: [MessageId] = [] + var addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [] if !wasOperationScheduledMessageIds.isEmpty { let existingIds = transaction.filterStoredMessageIds(Set(wasOperationScheduledMessageIds)) @@ -3228,16 +3229,19 @@ func replayFinalState( return state }) } - case let .UpdateMessageReactions(messageId, reactions): + case let .UpdateMessageReactions(messageId, reactions, eventTimestamp): + var generatedEvent: (reactionAuthor: Peer, message: Message, timestamp: Int32)? transaction.updateMessage(messageId, update: { currentMessage in var updatedReactions = ReactionsMessageAttribute(apiReactions: reactions) let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) var attributes = currentMessage.attributes + var previousReactions: ReactionsMessageAttribute? var added = false loop: for j in 0 ..< attributes.count { if let attribute = attributes[j] as? ReactionsMessageAttribute { added = true + previousReactions = attribute updatedReactions = attribute.withUpdatedResults(reactions) if updatedReactions == attribute { @@ -3251,8 +3255,44 @@ 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) + } + } + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) + if let generatedEvent = generatedEvent { + addedReactionEvents.append(generatedEvent) + } } } @@ -3624,5 +3664,5 @@ func replayFinalState( requestChatListFiltersSync(transaction: transaction) } - return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates) + return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedReactionEvents: addedReactionEvents, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates) } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index 1f27657478..a832e6a134 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -105,6 +105,11 @@ public final class AccountStateManager { return self.notificationMessagesPipe.signal() } + private let reactionNotificationsPipe = ValuePipe<[(reactionAuthor: Peer, message: Message)]>() + public var reactionNotifications: Signal<[(reactionAuthor: Peer, message: Message)], NoError> { + return self.reactionNotificationsPipe.signal() + } + private let displayAlertsPipe = ValuePipe<[(text: String, isDropAuth: Bool)]>() public var displayAlerts: Signal<[(text: String, isDropAuth: Bool)], NoError> { return self.displayAlertsPipe.signal() @@ -739,6 +744,19 @@ public final class AccountStateManager { completed() }) + let timestamp = Int32(Date().timeIntervalSince1970) + let minReactionTimestamp = timestamp - 20 + let reactionEvents = events.addedReactionEvents.compactMap { event -> (reactionAuthor: Peer, message: Message)? in + if event.timestamp >= minReactionTimestamp { + return (event.reactionAuthor, event.message) + } else { + return nil + } + } + if !reactionEvents.isEmpty { + self.reactionNotificationsPipe.putNext(reactionEvents) + } + if !events.displayAlerts.isEmpty { self.displayAlertsPipe.putNext(events.displayAlerts) } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index ef7cc54a6f..066cdf0c7b 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -231,15 +231,20 @@ private func synchronizeMessageReactions(transaction: Transaction, postbox: Post public extension EngineMessageReactionListContext.State { init(message: EngineMessage, reaction: String?) { - var totalCount: Int = 0 + var totalCount = 0 + var hasOutgoingReaction = false if let reactionsAttribute = message._asMessage().reactionsAttribute { for messageReaction in reactionsAttribute.reactions { if reaction == nil || messageReaction.value == reaction { + if messageReaction.isSelected { + hasOutgoingReaction = true + } totalCount += Int(messageReaction.count) } } } self.init( + hasOutgoingReaction: hasOutgoingReaction, totalCount: totalCount, items: [], canLoadMore: totalCount != 0 @@ -272,15 +277,18 @@ public final class EngineMessageReactionListContext { } public struct State: Equatable { + public var hasOutgoingReaction: Bool public var totalCount: Int public var items: [Item] public var canLoadMore: Bool public init( + hasOutgoingReaction: Bool, totalCount: Int, items: [Item], canLoadMore: Bool ) { + self.hasOutgoingReaction = hasOutgoingReaction self.totalCount = totalCount self.items = items self.canLoadMore = canLoadMore @@ -289,6 +297,7 @@ public final class EngineMessageReactionListContext { private final class Impl { struct InternalState: Equatable { + var hasOutgoingReaction: Bool var totalCount: Int var items: [Item] var canLoadMore: Bool @@ -315,7 +324,7 @@ public final class EngineMessageReactionListContext { self.reaction = reaction let initialState = EngineMessageReactionListContext.State(message: message, reaction: reaction) - self.state = InternalState(totalCount: initialState.totalCount, items: initialState.items, canLoadMore: true, nextOffset: nil) + self.state = InternalState(hasOutgoingReaction: initialState.hasOutgoingReaction, totalCount: initialState.totalCount, items: initialState.items, canLoadMore: true, nextOffset: nil) if initialState.canLoadMore { self.loadMore() @@ -344,10 +353,10 @@ public final class EngineMessageReactionListContext { } |> mapToSignal { inputPeer -> Signal in if message.id.namespace != Namespaces.Message.Cloud { - return .single(InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) + return .single(InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) } guard let inputPeer = inputPeer else { - return .single(InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) + return .single(InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) } var flags: Int32 = 0 if reaction != nil { @@ -391,9 +400,9 @@ public final class EngineMessageReactionListContext { } } - return InternalState(totalCount: Int(count), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset) + return InternalState(hasOutgoingReaction: false, totalCount: Int(count), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset) case .none: - return InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil) + return InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil) } } } @@ -438,6 +447,7 @@ public final class EngineMessageReactionListContext { self.impl.with { impl in disposable.set(impl.statePromise.get().start(next: { state in subscriber.putNext(State( + hasOutgoingReaction: state.hasOutgoingReaction, totalCount: state.totalCount, items: state.items, canLoadMore: state.canLoadMore