From 0cab0ca678020dae4bbfbda8325e8851f8801bf8 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 23 Jan 2024 17:52:21 +0100 Subject: [PATCH] [WIP] Saved messages --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../Sources/ChatController.swift | 6 + .../ChatListUI/Sources/ChatContextMenus.swift | 7 +- .../Sources/ReactionButtonListComponent.swift | 40 ++--- .../DeleteChatPeerActionSheetItem.swift | 8 +- submodules/Display/Source/ListView.swift | 89 +++++++--- .../Source/ListViewIntermediateState.swift | 1 + .../Postbox/Sources/MessageHistoryTable.swift | 14 ++ .../MessageHistoryTagsSummaryTable.swift | 1 - .../Postbox/Sources/MessageHistoryView.swift | 6 +- .../Sources/MessageHistoryViewState.swift | 48 ++++-- ...MessageHistoryCustomTagSummariesView.swift | 10 +- submodules/Postbox/Sources/Views.swift | 13 +- .../Sources/ReactionContextNode.swift | 3 + .../Source/Signal_Combine.swift | 6 +- .../SelectivePrivacySettingsController.swift | 2 +- .../Sources/ShareController.swift | 4 +- .../Sources/State/AccountTaskManager.swift | 2 +- .../Sources/State/AccountViewTracker.swift | 3 + ...anagedConsumePersonalMessagesActions.swift | 128 ++++++++------ .../Sources/State/MessageReactions.swift | 37 +++-- .../Sources/State/PendingMessageManager.swift | 30 ++++ .../Sources/State/SavedMessageTags.swift | 47 +++++- .../SyncCore/SyncCore_Namespaces.swift | 8 + .../TelegramEngine/Data/MessagesData.swift | 6 +- .../Messages/TelegramEngineMessages.swift | 19 +++ .../Stickers/TelegramEngineStickers.swift | 19 ++- submodules/TelegramUI/BUILD | 1 + .../ChatMessageAnimatedStickerItemNode.swift | 2 + .../ChatMessageAttachedContentNode.swift | 1 + .../Sources/ChatMessageBubbleItemNode.swift | 2 + .../ChatMessageContactBubbleContentNode.swift | 1 + .../ChatMessageDateAndStatusNode.swift | 15 +- ...ChatMessageGiveawayBubbleContentNode.swift | 1 + .../ChatMessageInstantVideoItemNode.swift | 1 + .../ChatMessageInteractiveFileNode.swift | 1 + ...atMessageInteractiveInstantVideoNode.swift | 1 + .../ChatMessageInteractiveMediaNode.swift | 1 + .../Sources/ChatMessageDateHeader.swift | 8 +- .../ChatMessageMapBubbleContentNode.swift | 1 + .../ChatMessagePollBubbleContentNode.swift | 1 + ...hatMessageReactionsFooterContentNode.swift | 19 ++- ...atMessageRestrictedBubbleContentNode.swift | 1 + .../Sources/ChatMessageStickerItemNode.swift | 2 + .../ChatMessageTextBubbleContentNode.swift | 1 + .../ChatRecentActionsHistoryTransition.swift | 110 ++++++------ .../Sources/ChatShareMessageTagView.swift | 1 + .../Sources/EmojiTextAttachmentView.swift | 110 ++++++++---- .../Sources/PeerInfoChatListPaneNode.swift | 7 +- .../PeerInfoScreen/Sources/PeerInfoData.swift | 17 +- .../Sources/StoryContainerScreen.swift | 77 ++++++--- ...UpdateChatPresentationInterfaceState.swift | 9 +- .../TelegramUI/Sources/ChatController.swift | 9 +- .../ChatControllerForwardMessages.swift | 135 +++++++++------ ...rollerOpenMessageReactionContextMenu.swift | 36 ++++ .../ChatControllerOpenMessageShareMenu.swift | 113 ++++++------- .../Sources/ChatHistoryListNode.swift | 57 ++++++- .../ChatInterfaceStateInputPanels.swift | 15 +- .../ChatInterfaceTitlePanelNodes.swift | 13 +- .../ChatSearchTitleAccessoryPanelNode.swift | 156 ++++++++++++++++-- .../Sources/ChatTextInputPanelNode.swift | 2 +- .../Sources/SharedAccountContext.swift | 2 +- 62 files changed, 1078 insertions(+), 411 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 53bdfbb7c0..4cd185b993 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10976,3 +10976,6 @@ Sorry for the inconvenience."; "Settings.Privacy.ReadTimePremiumActiveFooter" = "Because you are a Telegram Premium subscriber, you will see the last seen and read time of all users who are sharing it with you – even if you are hiding yours."; "Privacy.VoiceMessages.NonPremiumHelp" = "Subscribe to Telegram Premium to restrict who can send you voice or video messages.\n\n[What is Telegram Premium?]()"; + +"ChatList.DeleteSavedPeerMyNotesConfirmation" = "Are you sure you want to delete all messages from %@?"; +"ChatList.DeleteSavedPeerMyNotesConfirmationTitle" = "My Notes"; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index a9c229ab10..47b2ebd62e 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -41,6 +41,7 @@ public final class ChatMessageItemAssociatedData: Equatable { public let currentlyPlayingMessageId: EngineMessage.Index? public let isCopyProtectionEnabled: Bool public let availableReactions: AvailableReactions? + public let savedMessageTags: SavedMessageTags? public let defaultReaction: MessageReaction.Reaction? public let isPremium: Bool public let forceInlineReactions: Bool @@ -70,6 +71,7 @@ public final class ChatMessageItemAssociatedData: Equatable { currentlyPlayingMessageId: EngineMessage.Index? = nil, isCopyProtectionEnabled: Bool = false, availableReactions: AvailableReactions?, + savedMessageTags: SavedMessageTags?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, accountPeer: EnginePeer?, @@ -98,6 +100,7 @@ public final class ChatMessageItemAssociatedData: Equatable { self.currentlyPlayingMessageId = currentlyPlayingMessageId self.isCopyProtectionEnabled = isCopyProtectionEnabled self.availableReactions = availableReactions + self.savedMessageTags = savedMessageTags self.defaultReaction = defaultReaction self.isPremium = isPremium self.accountPeer = accountPeer @@ -154,6 +157,9 @@ public final class ChatMessageItemAssociatedData: Equatable { if lhs.availableReactions != rhs.availableReactions { return false } + if lhs.savedMessageTags != rhs.savedMessageTags { + return false + } if lhs.isPremium != rhs.isPremium { return false } diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 890c78ec2c..a1b840bd7d 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -854,7 +854,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: } } -public func savedMessagesPeerMenuItems(context: AccountContext, threadId: Int64, parentController: ViewController) -> Signal<[ContextMenuItem], NoError> { +public func savedMessagesPeerMenuItems(context: AccountContext, threadId: Int64, parentController: ViewController, deletePeerChat: @escaping (EnginePeer.Id) -> Void) -> Signal<[ContextMenuItem], NoError> { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) let strings = presentationData.strings @@ -888,6 +888,11 @@ public func savedMessagesPeerMenuItems(context: AccountContext, threadId: Int64, }) }))) + items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in + deletePeerChat(PeerId(threadId)) + f(.default) + }))) + return .single(items) } } diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index 1f2ef066a7..dd9be88e4d 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -379,7 +379,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { func drawContents(colors: Colors) { let backgroundColor: UIColor let foregroundColor: UIColor - if isExtracted { + if isExtracted && !layout.colors.isSelected { backgroundColor = UIColor(argb: colors.extractedBackground) foregroundColor = UIColor(argb: colors.extractedForeground) } else { @@ -416,7 +416,12 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { let isForegroundTransparent = foregroundColor.alpha < 1.0 context.setBlendMode(isForegroundTransparent ? .copy : .normal) - let textOrigin: CGFloat = 36.0 + let textOrigin: CGFloat + if layout.isTag { + textOrigin = 30.0 + } else { + textOrigin = 36.0 + } var rightTextOrigin = textOrigin + totalComponentWidth @@ -642,26 +647,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { let boundingImageSize = CGSize(width: 20.0, height: 20.0) let imageSize: CGSize = boundingImageSize - /*if let file = spec.component.reaction.centerAnimation { - let defaultImageSize = CGSize(width: boundingImageSize.width + floor(boundingImageSize.width * 0.5 * 2.0), height: boundingImageSize.height + floor(boundingImageSize.height * 0.5 * 2.0)) - imageSize = file.dimensions?.cgSize.aspectFitted(defaultImageSize) ?? defaultImageSize - } else { - imageSize = boundingImageSize - }*/ var counterComponents: [String] = [] - for character in countString(Int64(spec.component.count)) { - counterComponents.append(String(character)) - } - - /*#if DEBUG - if spec.component.count % 2 == 0 { - counterComponents.removeAll() - for character in "123.5K" { + var hasTitle = false + if let title = spec.component.reaction.title, !title.isEmpty { + hasTitle = true + counterComponents.append(title) + } else { + for character in countString(Int64(spec.component.count)) { counterComponents.append(String(character)) } } - #endif*/ let backgroundColor = spec.component.chosenOrder != nil ? spec.component.colors.selectedBackground : spec.component.colors.deselectedBackground @@ -672,7 +668,6 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { imageFrame = CGRect(origin: CGPoint(x: sideInsets + floorToScreenPixels((boundingImageSize.width - imageSize.width) / 2.0), y: floorToScreenPixels((height - imageSize.height) / 2.0)), size: imageSize) } - var counterLayout: CounterLayout? var size = CGSize(width: boundingImageSize.width + sideInsets * 2.0, height: height) @@ -683,7 +678,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { } else { size.width -= 2.0 } - } else if spec.component.isTag { + } else if spec.component.isTag && !hasTitle { size.width += 2.0 } else { let counterSpec = CounterLayout.Spec( @@ -1002,11 +997,13 @@ public final class ReactionButtonComponent: Equatable { public var value: MessageReaction.Reaction public var centerAnimation: TelegramMediaFile? public var animationFileId: Int64? + public var title: String? - public init(value: MessageReaction.Reaction, centerAnimation: TelegramMediaFile?, animationFileId: Int64?) { + public init(value: MessageReaction.Reaction, centerAnimation: TelegramMediaFile?, animationFileId: Int64?, title: String?) { self.value = value self.centerAnimation = centerAnimation self.animationFileId = animationFileId + self.title = title } public static func ==(lhs: Reaction, rhs: Reaction) -> Bool { @@ -1019,6 +1016,9 @@ public final class ReactionButtonComponent: Equatable { if lhs.animationFileId != rhs.animationFileId { return false } + if lhs.title != rhs.title { + return false + } return true } } diff --git a/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift b/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift index 11e2107c4e..7d6291b9aa 100644 --- a/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift +++ b/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift @@ -133,8 +133,12 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { text = strings.ChatList_DeleteChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) } case .deleteSavedPeer: - let peerTitle = peer.displayTitle(strings: strings, displayOrder: nameOrder) - text = strings.ChatList_DeleteSavedPeerConfirmation(peerTitle) + if peer.id == context.account.peerId { + text = strings.ChatList_DeleteSavedPeerMyNotesConfirmation(strings.ChatList_DeleteSavedPeerMyNotesConfirmationTitle) + } else { + let peerTitle = peer.displayTitle(strings: strings, displayOrder: nameOrder) + text = strings.ChatList_DeleteSavedPeerConfirmation(peerTitle) + } case let .clearHistory(canClearCache): if peer.id == context.account.peerId { text = PresentationStrings.FormattedString(string: strings.ChatList_ClearSavedMessagesConfirmation, ranges: []) diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 2cfe0d9871..a09ffe2c8f 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -2154,7 +2154,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture let beginReplay = { [weak self] in if let strongSelf = self { - strongSelf.replayOperations(animated: animated, animateAlpha: options.contains(.AnimateAlpha), animateCrossfade: options.contains(.AnimateCrossfade), synchronous: options.contains(.Synchronous), synchronousLoads: options.contains(.PreferSynchronousResourceLoading), animateTopItemVerticalOrigin: options.contains(.AnimateTopItemPosition), operations: updatedOperations, requestItemInsertionAnimationsIndices: options.contains(.RequestItemInsertionAnimations) ? insertedIndexSet : Set(), scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemIndex: stationaryItemIndex, updateOpaqueState: updateOpaqueState, completion: { + strongSelf.replayOperations(animated: animated, animateAlpha: options.contains(.AnimateAlpha), animateCrossfade: options.contains(.AnimateCrossfade), animateFullTransition: options.contains(.AnimateFullTransition), synchronous: options.contains(.Synchronous), synchronousLoads: options.contains(.PreferSynchronousResourceLoading), animateTopItemVerticalOrigin: options.contains(.AnimateTopItemPosition), operations: updatedOperations, requestItemInsertionAnimationsIndices: options.contains(.RequestItemInsertionAnimations) ? insertedIndexSet : Set(), scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemIndex: stationaryItemIndex, updateOpaqueState: updateOpaqueState, completion: { if options.contains(.PreferSynchronousDrawing) { self?.recursivelyEnsureDisplaySynchronously(true) } @@ -2377,7 +2377,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - private func insertNodeAtIndex(animated: Bool, animateAlpha: Bool, forceAnimateInsertion: Bool, previousFrame: CGRect?, nodeIndex: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal?, (ListViewItemApply) -> Void), timestamp: Double, listInsets: UIEdgeInsets, visibleBounds: CGRect) { + private func insertNodeAtIndex(animated: Bool, animateAlpha: Bool, animateFullTransition: Bool, forceAnimateInsertion: Bool, previousFrame: CGRect?, nodeIndex: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal?, (ListViewItemApply) -> Void), timestamp: Double, listInsets: UIEdgeInsets, visibleBounds: CGRect) { let insertionOrigin = self.referencePointForInsertionAtIndex(nodeIndex) let nodeOrigin: CGPoint @@ -2520,11 +2520,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture node.animateInsertion(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), short: false) } } - } else if animateAlpha && previousFrame == nil { - if forceAnimateInsertion { - node.animateInsertion(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), short: true) - } else { - node.animateAdded(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor()) + } else if animateAlpha { + if previousFrame == nil { + if forceAnimateInsertion { + node.animateInsertion(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), short: true) + } else if animateFullTransition { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + node.layer.animateScale(from: 0.7, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + } else { + node.animateAdded(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor()) + } } } @@ -2612,7 +2617,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - private func replayOperations(animated: Bool, animateAlpha: Bool, animateCrossfade: Bool, synchronous: Bool, synchronousLoads: Bool, animateTopItemVerticalOrigin: Bool, operations: [ListViewStateOperation], requestItemInsertionAnimationsIndices: Set, scrollToItem originalScrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemIndex: Int?, updateOpaqueState: Any?, completion: () -> Void) { + private func replayOperations(animated: Bool, animateAlpha: Bool, animateCrossfade: Bool, animateFullTransition: Bool, synchronous: Bool, synchronousLoads: Bool, animateTopItemVerticalOrigin: Bool, operations: [ListViewStateOperation], requestItemInsertionAnimationsIndices: Set, scrollToItem originalScrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemIndex: Int?, updateOpaqueState: Any?, completion: () -> Void) { var scrollToItem: ListViewScrollToItem? var isExperimentalSnapToScrollToItem = false if let originalScrollToItem = originalScrollToItem { @@ -2679,6 +2684,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture takenPreviousNodes.insert(node.syncWith({ $0 })) } } + var removedPreviousNodes = Set() let lowestNodeToInsertBelow = self.lowestNodeToInsertBelow() var hadInserts = false @@ -2706,7 +2712,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture updatedPreviousFrame = nil } - self.insertNodeAtIndex(animated: nodeAnimated, animateAlpha: animateAlpha, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) + self.insertNodeAtIndex(animated: nodeAnimated, animateAlpha: animateAlpha, animateFullTransition: animateFullTransition, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) hadInserts = true hadChangesToItemNodes = true if let _ = updatedPreviousFrame { @@ -2767,10 +2773,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if let height = height, let previousLayout = previousLayout { if takenPreviousNodes.contains(referenceNode) { let tempNode = ListViewTempItemNode(layerBacked: true) - self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: tempNode, layout: ListViewItemNodeLayout(contentSize: CGSize(width: self.visibleSize.width, height: height), insets: UIEdgeInsets()), apply: { return (nil, { _ in }) }, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) + self.insertNodeAtIndex(animated: false, animateAlpha: false, animateFullTransition: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: tempNode, layout: ListViewItemNodeLayout(contentSize: CGSize(width: self.visibleSize.width, height: height), insets: UIEdgeInsets()), apply: { return (nil, { _ in }) }, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) } else { referenceNode.index = nil - self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: referenceNode, layout: previousLayout, apply: { return (nil, { _ in }) }, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) + self.insertNodeAtIndex(animated: false, animateAlpha: false, animateFullTransition: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: referenceNode, layout: previousLayout, apply: { return (nil, { _ in }) }, timestamp: timestamp, listInsets: listInsets, visibleBounds: visibleBounds) if let verticalScrollIndicator = self.verticalScrollIndicator { self.insertSubnode(referenceNode, belowSubnode: verticalScrollIndicator) } else { @@ -2819,8 +2825,18 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } } - - self.removeItemNodeAtIndex(index) + + if animateFullTransition { + for (previousNode, previousFrame) in previousApparentFrames { + if previousNode === self.itemNodes[index] { + removedPreviousNodes.insert(previousNode) + self.itemNodes[index].frame = previousFrame + break + } + } + } + + self.removeItemNodeAtIndex(index, animateFullTransition: animateFullTransition) hadChangesToItemNodes = true case let .UpdateLayout(index, layout, apply): let node = self.itemNodes[index] @@ -3275,6 +3291,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self.highlightedItemIndex = nil } + if animateFullTransition { + for (previousNode, previousFrame) in previousApparentFrames { + if !takenPreviousNodes.contains(previousNode) && !removedPreviousNodes.contains(previousNode) { + previousNode.layer.animatePosition(from: CGPoint(x: 0.0, y: previousFrame.minY - previousNode.frame.minY), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } + } + } + if let scrollToItem = scrollToItem, scrollToItem.animated { if self.itemNodes.count != 0 { var offset: CGFloat? @@ -3615,16 +3639,33 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - private func removeItemNodeAtIndex(_ index: Int) { + private func removeItemNodeAtIndex(_ index: Int, animateFullTransition: Bool) { let node = self.itemNodes[index] self.itemNodes.remove(at: index) - node.visibility = .none - node.removeFromSupernode() - node.extractedBackgroundNode?.removeFromSupernode() - node.accessoryItemNode?.removeFromSupernode() - node.setAccessoryItemNode(nil, leftInset: self.insets.left, rightInset: self.insets.right) - node.headerAccessoryItemNode?.removeFromSupernode() - node.headerAccessoryItemNode = nil + + if animateFullTransition { + node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in + guard let node else { + return + } + node.visibility = .none + node.removeFromSupernode() + node.extractedBackgroundNode?.removeFromSupernode() + node.accessoryItemNode?.removeFromSupernode() + node.setAccessoryItemNode(nil, leftInset: self.insets.left, rightInset: self.insets.right) + node.headerAccessoryItemNode?.removeFromSupernode() + node.headerAccessoryItemNode = nil + }) + node.layer.animateScale(from: 1.0, to: 0.001, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } else { + node.visibility = .none + node.removeFromSupernode() + node.extractedBackgroundNode?.removeFromSupernode() + node.accessoryItemNode?.removeFromSupernode() + node.setAccessoryItemNode(nil, leftInset: self.insets.left, rightInset: self.insets.right) + node.headerAccessoryItemNode?.removeFromSupernode() + node.headerAccessoryItemNode = nil + } } private var nextHeaderSpaceAffinity: Int = 0 @@ -4229,7 +4270,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture while i < self.itemNodes.count { let node = self.itemNodes[i] if node.index == nil && node.apparentHeight <= CGFloat.ulpOfOne { - self.removeItemNodeAtIndex(i) + self.removeItemNodeAtIndex(i, animateFullTransition: false) } else { i += 1 } @@ -4243,10 +4284,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture var updatedOperations = operations updatedState.removeInvisibleNodes(&updatedOperations) if synchronous { - self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion) + self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, animateFullTransition: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion) } else { self.dispatchOnVSync { - self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion) + self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, animateFullTransition: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion) } } } diff --git a/submodules/Display/Source/ListViewIntermediateState.swift b/submodules/Display/Source/ListViewIntermediateState.swift index 70680497eb..89da117cd2 100644 --- a/submodules/Display/Source/ListViewIntermediateState.swift +++ b/submodules/Display/Source/ListViewIntermediateState.swift @@ -129,6 +129,7 @@ public struct ListViewDeleteAndInsertOptions: OptionSet { public static let PreferSynchronousResourceLoading = ListViewDeleteAndInsertOptions(rawValue: 128) public static let AnimateCrossfade = ListViewDeleteAndInsertOptions(rawValue: 256) public static let ForceUpdate = ListViewDeleteAndInsertOptions(rawValue: 512) + public static let AnimateFullTransition = ListViewDeleteAndInsertOptions(rawValue: 1024) } public struct ListViewUpdateSizeAndInsets { diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index e0248035fe..599aafa9d9 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -301,6 +301,12 @@ final class MessageHistoryTable: Table { } self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + + if let threadId = message.threadId { + self.threadsTable.add(threadId: threadId, index: message.index) + + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } } let globalTags = message.globalTags.rawValue @@ -1386,6 +1392,10 @@ final class MessageHistoryTable: Table { } self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + + if let threadId = message.threadId { + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: customTag), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } } for tag in message.globalTags { self.globalTagsTable.remove(tag, index: index) @@ -1619,6 +1629,8 @@ final class MessageHistoryTable: Table { self.customTagTable.remove(threadId: nil, tag: customTag, index: index) if let threadId = previousMessage.threadId { self.customTagTable.remove(threadId: threadId, tag: customTag, index: index) + + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, customTag: customTag), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace, customTag: customTag), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) @@ -1627,6 +1639,8 @@ final class MessageHistoryTable: Table { self.customTagTable.add(threadId: nil, tag: customTag, index: message.index) if let threadId = message.threadId { self.customTagTable.add(threadId: threadId, tag: customTag, index: message.index) + + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) diff --git a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift index e1148ef0d5..2ec1ad130c 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift @@ -218,7 +218,6 @@ 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) } else { self.set(key, summary: current.withAddedCount(-1), updatedSummaries: &updatedSummaries) } diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 91581bce14..ee55e89dec 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -355,7 +355,11 @@ final class MutableMessageHistoryView { self.combinedReadStates = combinedReadStates self.transientReadStates = transientReadStates self.tag = tag - self.appendMessagesFromTheSameGroup = appendMessagesFromTheSameGroup + if case .customTag = tag { + self.appendMessagesFromTheSameGroup = true + } else { + self.appendMessagesFromTheSameGroup = appendMessagesFromTheSameGroup + } self.namespaces = namespaces self.fillCount = count self.topTaggedMessages = topTaggedMessages diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index c69f3a4562..4e7bd89a52 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -60,7 +60,7 @@ private extension MessageHistoryInput { case let .tag(value): shouldAddFromSameGroup = value.appendMessagesFromTheSameGroup case .customTag: - shouldAddFromSameGroup = false + shouldAddFromSameGroup = true } } if shouldAddFromSameGroup { @@ -75,20 +75,48 @@ private extension MessageHistoryInput { if var group = postbox.messageHistoryTable.getMessageGroup(at: items[index].index, limit: 20), group.count > 1 { switch direction { case .lowToHigh: - group.sort(by: { lhs, rhs in - return lhs.index < rhs.index - }) + group = group.filter { item in + if includeFrom { + return item.index >= fromIndex && item.index < toIndex + } else { + return item.index > fromIndex && item.index < toIndex + } + } case .highToLow: - group.sort(by: { lhs, rhs in - return lhs.index > rhs.index - }) + group = group.filter { item in + if includeFrom { + return item.index >= toIndex && item.index < fromIndex + } else { + return item.index > toIndex && item.index < fromIndex + } + } } - items.replaceSubrange(index ..< index + 1, with: group) + + items.remove(at: index) + var insertIndex = index + for item in group { + if !items.contains(where: { $0.id == item.id }) { + items.insert(item, at: insertIndex) + insertIndex += 1 + } + } + switch direction { case .lowToHigh: - items.removeFirst(group.count - 1) + items.sort(by: { $0.index < $1.index }) case .highToLow: - items.removeLast(group.count - 1) + items.sort(by: { $0.index > $1.index }) + } + assert(Set(items.map({ $0.stableId })).count == items.count) + + if items.count > limit { + let overLimit = limit - items.count + switch direction { + case .lowToHigh: + items.removeFirst(overLimit) + case .highToLow: + items.removeLast(overLimit) + } } } } diff --git a/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift b/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift index e7be5c75fa..f356e5c8a1 100644 --- a/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift +++ b/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift @@ -2,12 +2,14 @@ import Foundation final class MutableMessageHistoryCustomTagSummariesView: MutablePostboxView { private let peerId: PeerId + private let threadId: Int64? private let namespace: MessageId.Namespace fileprivate var tags: [MemoryBuffer: Int] = [:] - init(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace) { + init(postbox: PostboxImpl, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) { self.peerId = peerId + self.threadId = threadId self.namespace = namespace self.reload(postbox: postbox) @@ -16,8 +18,8 @@ final class MutableMessageHistoryCustomTagSummariesView: MutablePostboxView { private func reload(postbox: PostboxImpl) { self.tags.removeAll() - for tag in postbox.messageHistoryTagsSummaryTable.getCustomTags(tag: [], peerId: self.peerId, threadId: nil, namespace: self.namespace) { - if let summary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: [], peerId: self.peerId, threadId: nil, namespace: self.namespace, customTag: tag)) { + for tag in postbox.messageHistoryTagsSummaryTable.getCustomTags(tag: [], peerId: self.peerId, threadId: self.threadId, namespace: self.namespace) { + if let summary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: [], peerId: self.peerId, threadId: self.threadId, namespace: self.namespace, customTag: tag)) { if summary.count > 0 { self.tags[tag] = Int(summary.count) } @@ -29,7 +31,7 @@ final class MutableMessageHistoryCustomTagSummariesView: MutablePostboxView { var hasChanges = false for key in transaction.currentUpdatedMessageTagSummaries.keys { - if key.peerId == self.peerId && key.namespace == self.namespace && key.customTag != nil { + if key.peerId == self.peerId && key.namespace == self.namespace && key.customTag != nil && key.threadId == self.threadId { hasChanges = true break } diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index ffbd9a35ac..aed2fb3e30 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -13,7 +13,7 @@ public enum PostboxViewKey: Hashable { case invalidatedMessageHistoryTagSummaries(peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace) case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, customTag: MemoryBuffer?) - case historyCustomTagSummariesView(peerId: PeerId, namespace: MessageId.Namespace) + case historyCustomTagSummariesView(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) case cachedPeerData(peerId: PeerId) case unreadCounts(items: [UnreadMessageCountsItem]) case combinedReadState(peerId: PeerId, handleThreads: Bool) @@ -85,8 +85,9 @@ public enum PostboxViewKey: Hashable { hasher.combine(threadId) hasher.combine(namespace) hasher.combine(customTag) - case let .historyCustomTagSummariesView(peerId, namespace): + case let .historyCustomTagSummariesView(peerId, threadId, namespace): hasher.combine(peerId) + hasher.combine(threadId) hasher.combine(namespace) case let .cachedPeerData(peerId): hasher.combine(peerId) @@ -241,8 +242,8 @@ public enum PostboxViewKey: Hashable { } else { return false } - case let .historyCustomTagSummariesView(peerId, namespace): - if case .historyCustomTagSummariesView(peerId, namespace) = rhs { + case let .historyCustomTagSummariesView(peerId, threadId, namespace): + if case .historyCustomTagSummariesView(peerId, threadId, namespace) = rhs { return true } else { return false @@ -487,8 +488,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace) case let .historyTagSummaryView(tag, peerId, threadId, namespace, customTag): return MutableMessageHistoryTagSummaryView(postbox: postbox, tag: tag, peerId: peerId, threadId: threadId, namespace: namespace, customTag: customTag) - case let .historyCustomTagSummariesView(peerId, namespace): - return MutableMessageHistoryCustomTagSummariesView(postbox: postbox, peerId: peerId, namespace: namespace) + case let .historyCustomTagSummariesView(peerId, threadId, namespace): + return MutableMessageHistoryCustomTagSummariesView(postbox: postbox, peerId: peerId, threadId: threadId, namespace: namespace) case let .cachedPeerData(peerId): return MutableCachedPeerDataView(postbox: postbox, peerId: peerId) case let .unreadCounts(items): diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index aaa964e47c..3e7b5cf329 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -2431,6 +2431,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { case .ended: let point = recognizer.location(in: self.view) + if self.isExpanded { + return + } if let expandItemView = self.expandItemView, expandItemView.bounds.contains(self.view.convert(point, to: self.expandItemView)) { self.animateFromExtensionDistance = self.contentTopInset * 2.0 + self.extensionDistance self.contentTopInset = 0.0 diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift index 27cab69423..a3eaabd3ed 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift @@ -214,7 +214,11 @@ public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal, _ s7: Signal, _ s8: Signal, _ s9: Signal, _ s10: Signal, _ s11: Signal, _ s12: Signal, _ s13: Signal, _ s14: Signal, _ s15: Signal, _ s16: Signal, _ s17: Signal, _ s18: Signal, _ s19: Signal, _ s20: Signal, _ s21: Signal, _ s22: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22), E> { + return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22)], combine: { values in + return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22) + }, initialValues: [:], queue: queue) +} public func combineLatest(queue: Queue? = nil, _ signals: [Signal]) -> Signal<[T], E> { if signals.count == 0 { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index 1208292028..f0dba91eb5 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -473,7 +473,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { if isLocked { arguments.displayLockedInfo() } else { - arguments.updateType(.contacts) + arguments.updateType(.nobody) } }) case let .settingInfo(_, text, link): diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 63fbb73e3a..07c730b300 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -2275,7 +2275,9 @@ public final class ShareController: ViewController { } } - messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) + let correlationId = Int64.random(in: Int64.min ... Int64.max) + correlationIds.append(correlationId) + messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: correlationId)) } messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messagesToEnqueue)) diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index 9ae1fa5e64..b438b5d19c 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -89,7 +89,7 @@ final class AccountTaskManager { tasks.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedApplyPendingScheduledMessagesActions(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start()) tasks.add(managedSynchronizeAvailableReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) - tasks.add(managedSynchronizeSavedMessageTags(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) + tasks.add(managedSynchronizeSavedMessageTags(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start()) tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start()) tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start()) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 6b36ad4e3b..defdf6d8c8 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1732,11 +1732,13 @@ public final class AccountViewTracker { if context.subscribers.isEmpty { if let account = self.account { let queue = self.queue + Logger.shared.log("AccountViewTracker", "polledChannel: \(peerId) add keep polling") context.disposable.set(keepPollingChannel(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, peerId: peerId, stateManager: account.stateManager).start(next: { [weak context] isValidForTimeout in queue.async { guard let context = context else { return } + Logger.shared.log("AccountViewTracker", "polledChannel: \(peerId) set context isUpdated true for \(isValidForTimeout) seconds") context.isUpdated.set( .single(true) |> then( @@ -1756,6 +1758,7 @@ public final class AccountViewTracker { if let context = self.channelPollingContexts[peerId] { context.subscribers.remove(index) if context.subscribers.isEmpty { + Logger.shared.log("AccountViewTracker", "polledChannel: \(peerId) remove keep polling") context.disposable.set(nil) } } diff --git a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift index 9289f64799..0f75973ee7 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift @@ -461,17 +461,17 @@ func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Net for (entry, disposable) in beginValidateOperations { if entry.key.customTag != nil { if peerId == stateManager.accountPeerId { - let signal = synchronizeSavedMessageTags(postbox: postbox, network: network, peerId: peerId) + let signal = synchronizeSavedMessageTags(postbox: postbox, network: network, peerId: peerId, threadId: entry.key.threadId) |> map { _ -> Void in } |> then(postbox.transaction { transaction -> Void in - transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: []) + transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: entry.key.threadId, namespace: Namespaces.Message.Cloud, tagMask: []) }) disposable.set(signal.start()) } else { assertionFailure() let signal = postbox.transaction { transaction -> Void in - transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: []) + transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: entry.key.threadId, namespace: Namespaces.Message.Cloud, tagMask: []) } disposable.set(signal.start()) } @@ -529,61 +529,93 @@ private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Netw |> switchToLatest } -private func synchronizeSavedMessageTags(postbox: Postbox, network: Network, peerId: PeerId) -> Signal { - return (network.request(Api.functions.messages.getSavedReactionTags(hash: 0)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) +private func synchronizeSavedMessageTags(postbox: Postbox, network: Network, peerId: PeerId, threadId: Int64?) -> Signal { + let key: PostboxViewKey = .pendingMessageActions(type: .updateReaction) + let waitForApplySignal: Signal = postbox.combinedView(keys: [key]) + |> map { views -> Bool in + guard let view = views.views[key] as? PendingMessageActionsView else { + return false + } + + for entry in view.entries { + if entry.id.peerId == peerId { + return false + } + } + + return true } - |> mapToSignal { result -> Signal in - guard let result = result else { + |> filter { $0 } + |> take(1) + |> ignoreValues + + let updateSignal: Signal = (postbox.transaction { transaction -> Bool in + return transaction.getPreferencesEntry(key: PreferencesKeys.didCacheSavedMessageTags(threadId: threadId)) != nil + } + |> mapToSignal { alreadyCached -> Signal in + if alreadyCached { return .complete() } - switch result { - case .savedReactionTagsNotModified: - return .complete() - case let .savedReactionTags(tags, _): - var customFileIds: [Int64] = [] - var parsedTags: [SavedMessageTags.Tag] = [] - for tag in tags { - switch tag { - case let .savedReactionTag(_, reaction, title, count): - guard let reaction = MessageReaction.Reaction(apiReaction: reaction) else { - continue + return network.request(Api.functions.messages.getSavedReactionTags(hash: 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .complete() + } + + switch result { + case .savedReactionTagsNotModified: + return .complete() + case let .savedReactionTags(tags, _): + var customFileIds: [Int64] = [] + var parsedTags: [SavedMessageTags.Tag] = [] + for tag in tags { + switch tag { + case let .savedReactionTag(_, reaction, title, count): + guard let reaction = MessageReaction.Reaction(apiReaction: reaction) else { + continue + } + parsedTags.append(SavedMessageTags.Tag( + reaction: reaction, + title: title, + count: Int(count) + )) + + if case let .custom(fileId) = reaction { + customFileIds.append(fileId) + } } - parsedTags.append(SavedMessageTags.Tag( - reaction: reaction, - title: title, - count: Int(count) - )) + } + + let _ = customFileIds + + return postbox.transaction { transaction -> Void in + let previousTags = transaction.getMessageTagSummaryCustomTags(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud) - if case let .custom(fileId) = reaction { - customFileIds.append(fileId) + let topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud)?.id ?? 1 + + var validTags: [MemoryBuffer] = [] + for tag in parsedTags { + let customTag = ReactionsMessageAttribute.messageTag(reaction: tag.reaction) + validTags.append(customTag) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: customTag, count: Int32(tag.count), maxId: topMessageId) } - } - } - - let _ = customFileIds - - return postbox.transaction { transaction -> Void in - let previousTags = transaction.getMessageTagSummaryCustomTags(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud) - - let topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud)?.id ?? 1 - - var validTags: [MemoryBuffer] = [] - for tag in parsedTags { - let customTag = ReactionsMessageAttribute.messageTag(reaction: tag.reaction) - validTags.append(customTag) - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: customTag, count: Int32(tag.count), maxId: topMessageId) - } - for tag in previousTags { - if !validTags.contains(tag) { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: tag, count: 0, maxId: topMessageId) + for tag in previousTags { + if !validTags.contains(tag) { + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: tag, count: 0, maxId: topMessageId) + } } + + transaction.setPreferencesEntry(key: PreferencesKeys.didCacheSavedMessageTags(threadId: threadId), value: PreferencesEntry(data: Data())) } + |> ignoreValues } - |> ignoreValues } }) + + return waitForApplySignal |> then(updateSignal |> delay(1.0, queue: .concurrentDefaultQueue())) } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 607ef4c065..df29cf521f 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -67,24 +67,27 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes } if storeAsRecentlyUsed { - let effectiveReactions = currentMessage.effectiveReactions(isTags: currentMessage.areReactionsTags(accountPeerId: account.peerId)) ?? [] - for updatedReaction in reactions { - if !effectiveReactions.contains(where: { $0.value == updatedReaction.reaction && $0.isSelected }) { - let recentReactionItem: RecentReactionItem - switch updatedReaction { - case let .builtin(value): - recentReactionItem = RecentReactionItem(.builtin(value)) - case let .custom(fileId, file): - if let file = file ?? (transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile) { - recentReactionItem = RecentReactionItem(.custom(file)) - } else { - continue + let isTags = currentMessage.areReactionsTags(accountPeerId: account.peerId) + if !isTags { + let effectiveReactions = currentMessage.effectiveReactions(isTags: isTags) ?? [] + for updatedReaction in reactions { + if !effectiveReactions.contains(where: { $0.value == updatedReaction.reaction && $0.isSelected }) { + let recentReactionItem: RecentReactionItem + switch updatedReaction { + case let .builtin(value): + recentReactionItem = RecentReactionItem(.builtin(value)) + case let .custom(fileId, file): + if let file = file ?? (transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile) { + recentReactionItem = RecentReactionItem(.custom(file)) + } else { + continue + } + } + + if let entry = CodableEntry(recentReactionItem) { + let itemEntry = OrderedItemListEntry(id: recentReactionItem.id.rawValue, contents: entry) + transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentReactions, item: itemEntry, removeTailIfCountExceeds: 50) } - } - - if let entry = CodableEntry(recentReactionItem) { - let itemEntry = OrderedItemListEntry(id: recentReactionItem.id.rawValue, contents: entry) - transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentReactions, item: itemEntry, removeTailIfCountExceeds: 50) } } } diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 371e864c9d..262224da8b 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -149,6 +149,10 @@ final class PendingMessageRequestDependencyTag: NetworkRequestDependencyTag { } } +private final class CorrelationIdToSentMessageId { + var mapping: [Int64: MessageId] = [:] +} + public final class PendingMessageManager { private let network: Network private let postbox: Postbox @@ -174,6 +178,8 @@ public final class PendingMessageManager { var transformOutgoingMessageMedia: TransformOutgoingMessageMedia? + private let correlationIdToSentMessageId: Atomic = Atomic(value: CorrelationIdToSentMessageId()) + init(network: Network, postbox: Postbox, accountPeerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods, stateManager: AccountStateManager, localInputActivityManager: PeerInputActivityManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext) { Logger.shared.log("PendingMessageManager", "create instance") self.network = network @@ -1560,6 +1566,12 @@ public final class PendingMessageManager { var namespace = Namespaces.Message.Cloud if let apiMessage = apiMessage, let id = apiMessage.id(namespace: message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) { namespace = id.namespace + + if let attribute = message.attributes.first(where: { $0 is OutgoingMessageInfoAttribute }) as? OutgoingMessageInfoAttribute, let correlationId = attribute.correlationId { + self.correlationIdToSentMessageId.with { value in + value.mapping[correlationId] = id + } + } } return applyUpdateMessage(postbox: postbox, stateManager: stateManager, message: message, cacheReferenceKey: content.cacheReferenceKey, result: result, accountPeerId: self.accountPeerId) @@ -1586,6 +1598,20 @@ public final class PendingMessageManager { } } + if messages.count == result.messages.count { + for i in 0 ..< messages.count { + let message = messages[i] + let apiMessage = result.messages[i] + if let id = apiMessage.id(namespace: message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) { + if let attribute = message.attributes.first(where: { $0 is OutgoingMessageInfoAttribute }) as? OutgoingMessageInfoAttribute, let correlationId = attribute.correlationId { + self.correlationIdToSentMessageId.with { value in + value.mapping[correlationId] = id + } + } + } + } + } + return applyUpdateGroupMessages(postbox: postbox, stateManager: stateManager, messages: messages, result: result) |> afterDisposed { [weak self] in if let strongSelf = self { @@ -1665,4 +1691,8 @@ public final class PendingMessageManager { return disposable } } + + public func synchronouslyLookupCorrelationId(correlationId: Int64) -> MessageId? { + return self.correlationIdToSentMessageId.with { $0.mapping[correlationId] } + } } diff --git a/submodules/TelegramCore/Sources/State/SavedMessageTags.swift b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift index c6172a7552..9387979e63 100644 --- a/submodules/TelegramCore/Sources/State/SavedMessageTags.swift +++ b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift @@ -132,8 +132,27 @@ func _internal_setSavedMessageTags(transaction: Transaction, savedMessageTags: S } } -func managedSynchronizeSavedMessageTags(postbox: Postbox, network: Network) -> Signal { +func managedSynchronizeSavedMessageTags(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal { let poll = Signal { subscriber in + let key: PostboxViewKey = .pendingMessageActions(type: .updateReaction) + let waitForApplySignal: Signal = postbox.combinedView(keys: [key]) + |> map { views -> Bool in + guard let view = views.views[key] as? PendingMessageActionsView else { + return false + } + + for entry in view.entries { + if entry.id.peerId == accountPeerId { + return false + } + } + + return true + } + |> filter { $0 } + |> take(1) + |> ignoreValues + let signal: Signal = _internal_savedMessageTags(postbox: postbox) |> mapToSignal { current in return (network.request(Api.functions.messages.getSavedReactionTags(hash: current?.hash ?? 0)) @@ -187,7 +206,7 @@ func managedSynchronizeSavedMessageTags(postbox: Postbox, network: Network) -> S }) } - return signal.start(completed: { + return (waitForApplySignal |> then(signal)).start(completed: { subscriber.putCompletion() }) } @@ -201,3 +220,27 @@ func managedSynchronizeSavedMessageTags(postbox: Postbox, network: Network) -> S ) |> restart } + +func _internal_setSavedMessageTagTitle(account: Account, reaction: MessageReaction.Reaction, title: String?) -> Signal { + return account.postbox.transaction { transaction -> Void in + let value = _internal_savedMessageTags(transaction: transaction) ?? SavedMessageTags(hash: 0, tags: []) + var updatedTags = value.tags + if let index = updatedTags.firstIndex(where: { $0.reaction == reaction }) { + updatedTags[index] = SavedMessageTags.Tag(reaction: updatedTags[index].reaction, title: title, count: updatedTags[index].count) + } else { + updatedTags.append(SavedMessageTags.Tag(reaction: reaction, title: title, count: 0)) + } + _internal_setSavedMessageTags(transaction: transaction, savedMessageTags: SavedMessageTags(hash: 0, tags: updatedTags)) + } + |> mapToSignal { _ -> Signal in + var flags: Int32 = 0 + if title != nil { + flags |= 1 << 0 + } + return account.network.request(Api.functions.messages.updateSavedReactionTag(flags: flags, reaction: reaction.apiReaction, title: title)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } +} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index c8abd9c147..720a5ee8f9 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -277,6 +277,7 @@ private enum PreferencesKeyValues: Int32 { case globalPrivacySettings = 31 case storiesConfiguration = 32 case audioTranscriptionTrialState = 33 + case didCacheSavedMessageTagsPrefix = 34 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -447,6 +448,13 @@ public struct PreferencesKeys { key.setInt32(0, value: PreferencesKeyValues.audioTranscriptionTrialState.rawValue) return key }() + + public static func didCacheSavedMessageTags(threadId: Int64?) -> ValueBoxKey { + let key = ValueBoxKey(length: 4 + 8) + key.setInt32(0, value: PreferencesKeyValues.didCacheSavedMessageTagsPrefix.rawValue) + key.setInt64(4, value: threadId ?? 0) + return key + } } private enum SharedDataKeyValues: Int32 { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift index 39c1df9b28..e04aee6b80 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift @@ -392,13 +392,15 @@ public extension TelegramEngine.EngineData.Item { public typealias Result = [MessageReaction.Reaction: Int] fileprivate var peerId: EnginePeer.Id + fileprivate var threadId: Int64? - public init(peerId: EnginePeer.Id) { + public init(peerId: EnginePeer.Id, threadId: Int64?) { self.peerId = peerId + self.threadId = threadId } var key: PostboxViewKey { - return .historyCustomTagSummariesView(peerId: self.peerId, namespace: Namespaces.Message.Cloud) + return .historyCustomTagSummariesView(peerId: self.peerId, threadId: self.threadId, namespace: Namespaces.Message.Cloud) } func extract(view: PostboxView) -> Result { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index eb9368972a..b1ab2cf4e5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -1326,6 +1326,10 @@ public extension TelegramEngine { return self.account.stateManager.synchronouslyIsMessageDeletedInteractively(ids: ids) } + public func synchronouslyLookupCorrelationId(correlationId: Int64) -> EngineMessage.Id? { + return self.account.pendingMessageManager.synchronouslyLookupCorrelationId(correlationId: correlationId) + } + public func savedMessagesPeerListHead() -> Signal { return self.account.postbox.combinedView(keys: [.savedMessagesIndex(peerId: self.account.peerId)]) |> map { views -> EnginePeer.Id? in @@ -1341,6 +1345,21 @@ public extension TelegramEngine { } } + public func savedMessagesHasPeersOtherThanSaved() -> Signal { + return self.account.postbox.combinedView(keys: [.savedMessagesIndex(peerId: self.account.peerId)]) + |> map { views -> Bool in + //TODO:api optimize + guard let view = views.views[.savedMessagesIndex(peerId: self.account.peerId)] as? MessageHistorySavedMessagesIndexView else { + return false + } + if view.isLoading { + return false + } else { + return view.items.contains(where: { $0.peer?.id != self.account.peerId }) + } + } + } + public func savedMessagesPeersStats() -> Signal { return self.account.postbox.combinedView(keys: [.savedMessagesStats(peerId: self.account.peerId)]) |> map { views -> Int? in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 7011acd75e..7fabb0b1b4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -109,6 +109,19 @@ public extension TelegramEngine { return _internal_cachedAvailableReactions(postbox: self.account.postbox) } + public func savedMessageTagData() -> Signal { + return self.account.postbox.combinedView(keys: [PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())]) + |> mapToSignal { views -> Signal in + guard let views = views.views[PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())] as? CachedItemView else { + return .single(nil) + } + guard let savedMessageTags = views.value?.get(SavedMessageTags.self) else { + return .single(nil) + } + return .single(savedMessageTags) + } + } + public func savedMessageTags() -> Signal<([SavedMessageTags.Tag], [Int64: TelegramMediaFile]), NoError> { return self.account.postbox.combinedView(keys: [PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())]) |> mapToSignal { views -> Signal<([SavedMessageTags.Tag], [Int64: TelegramMediaFile]), NoError> in @@ -134,7 +147,11 @@ public extension TelegramEngine { } public func refreshSavedMessageTags() -> Signal { - return managedSynchronizeSavedMessageTags(postbox: self.account.postbox, network: self.account.network) + return managedSynchronizeSavedMessageTags(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId) + } + + public func setSavedMessageTagTitle(reaction: MessageReaction.Reaction, title: String?) -> Signal { + return _internal_setSavedMessageTagTitle(account: self.account, reaction: reaction, title: title) } public func emojiSearchCategories(kind: EmojiSearchCategories.Kind) -> Signal { diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index e9810f4c1a..b1d90fdf16 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -426,6 +426,7 @@ swift_library( "//submodules/TelegramUI/Components/VideoMessageCameraScreen", "//submodules/TelegramUI/Components/MediaScrubberComponent", "//submodules/TelegramUI/Components/Chat/ChatShareMessageTagView", + "//submodules/PromptUI", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "//build-system:ios_sim_arm64": [], diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 31efcae6fa..01fb584965 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1042,6 +1042,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, @@ -1255,6 +1256,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { presentationData: item.presentationData, presentationContext: item.controllerInteraction.presentationContext, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: reactions, message: item.message, accountPeer: item.associatedData.accountPeer, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 806fe1c87a..947fae6fb3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -654,6 +654,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { ), constrainedSize: CGSize(width: maxStatusContentWidth, height: CGFloat.greatestFiniteMagnitude), availableReactions: associatedData.availableReactions, + savedMessageTags: associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 86c067bae4..4a66ddd99a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2120,6 +2120,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, @@ -2438,6 +2439,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI presentationData: item.presentationData, presentationContext: item.controllerInteraction.presentationContext, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: bubbleReactions, message: item.message, accountPeer: item.associatedData.accountPeer, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index 4edfda2b05..9158abb2c9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -286,6 +286,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { layoutInput: .trailingContent(contentWidth: 1000.0, reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), constrainedSize: CGSize(width: constrainedSize.width - sideInsets, height: .greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 5c4fd88b1b..b81251875a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -225,6 +225,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var layoutInput: LayoutInput var constrainedSize: CGSize var availableReactions: AvailableReactions? + var savedMessageTags: SavedMessageTags? var reactions: [MessageReaction] var reactionPeers: [(MessageReaction.Reaction, EnginePeer)] var displayAllReactionPeers: Bool @@ -246,6 +247,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { layoutInput: LayoutInput, constrainedSize: CGSize, availableReactions: AvailableReactions?, + savedMessageTags: SavedMessageTags?, reactions: [MessageReaction], reactionPeers: [(MessageReaction.Reaction, EnginePeer)], displayAllReactionPeers: Bool, @@ -265,6 +267,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { self.type = type self.layoutInput = layoutInput self.availableReactions = availableReactions + self.savedMessageTags = savedMessageTags self.constrainedSize = constrainedSize self.reactions = reactions self.reactionPeers = reactionPeers @@ -803,11 +806,21 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } } + var title: String? + if arguments.areReactionsTags, let savedMessageTags = arguments.savedMessageTags { + for tag in savedMessageTags.tags { + if tag.reaction == reaction.value { + title = tag.title + } + } + } + return ReactionButtonsAsyncLayoutContainer.Reaction( reaction: ReactionButtonComponent.Reaction( value: reaction.value, centerAnimation: centerAnimation, - animationFileId: animationFileId + animationFileId: animationFileId, + title: title ), count: Int(reaction.count), peers: arguments.areReactionsTags ? [] : peers, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 1393d68bf5..53f4f6b2a0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -516,6 +516,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, layoutInput: .trailingContent(contentWidth: 1000.0, reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), constrainedSize: CGSize(width: constrainedSize.width - sideInsets, height: .greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index 52b09d2731..03f8d9b92e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -610,6 +610,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco presentationData: item.presentationData, presentationContext: item.controllerInteraction.presentationContext, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: reactions, message: item.message, accountPeer: item.associatedData.accountPeer, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index 52a13d531a..f360999223 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -933,6 +933,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { layoutInput: statusLayoutInput, constrainedSize: constrainedSize, availableReactions: arguments.associatedData.availableReactions, + savedMessageTags: arguments.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: arguments.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 84ca40fbbd..a924312e88 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -574,6 +574,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index 7560a4940c..7653903afb 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -869,6 +869,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: message, isPremium: associatedData.isPremium, forceInline: associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: nativeSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), availableReactions: associatedData.availableReactions, + savedMessageTags: associatedData.savedMessageTags, reactions: dateAndStatus.dateReactions, reactionPeers: dateAndStatus.dateReactionPeers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift index 63ae52c794..2a16f7d017 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift @@ -718,12 +718,16 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat override public func updateFlashingOnScrolling(_ isFlashingOnScrolling: Bool, animated: Bool) { } + + private var currentSelectionOffset: CGFloat = 0.0 public func updateSelectionState(animated: Bool) { + let currentSelectionOffset = self.currentSelectionOffset let offset: CGFloat = self.controllerInteraction?.selectionState != nil ? 42.0 : 0.0 + self.currentSelectionOffset = offset - let previousSubnodeTransform = self.subnodeTransform - self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0); + let previousSubnodeTransform = CATransform3DMakeTranslation(currentSelectionOffset, 0.0, 0.0) + self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0) if animated { self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index ae765fab0f..082f973291 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -273,6 +273,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index 16a2bc1c90..62a43d74ef 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1021,6 +1021,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { layoutInput: .trailingContent(contentWidth: 1000.0, reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), constrainedSize: textConstrainedSize, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift index c8fda01799..be4c8e881e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift @@ -58,6 +58,7 @@ public final class MessageReactionButtonsNode: ASDisplayNode { presentationData: ChatPresentationData, presentationContext: ChatPresentationContext, availableReactions: AvailableReactions?, + savedMessageTags: SavedMessageTags?, reactions: ReactionsMessageAttribute, accountPeer: EnginePeer?, message: Message, @@ -176,11 +177,21 @@ public final class MessageReactionButtonsNode: ASDisplayNode { } } + var title: String? + if isTag, let savedMessageTags { + for tag in savedMessageTags.tags { + if tag.reaction == reaction.value { + title = tag.title + } + } + } + return ReactionButtonsAsyncLayoutContainer.Reaction( reaction: ReactionButtonComponent.Reaction( value: reaction.value, centerAnimation: centerAnimation, - animationFileId: animationFileId + animationFileId: animationFileId, + title: title ), count: Int(reaction.count), peers: peers, @@ -515,7 +526,7 @@ public final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleConte context: item.context, presentationData: item.presentationData, presentationContext: item.controllerInteraction.presentationContext, - availableReactions: item.associatedData.availableReactions, reactions: reactionsAttribute, accountPeer: item.associatedData.accountPeer, message: item.message, alignment: .left, constrainedWidth: constrainedSize.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, type: item.message.effectivelyIncoming(item.context.account.peerId) ? .incoming : .outgoing) + availableReactions: item.associatedData.availableReactions, savedMessageTags: item.associatedData.savedMessageTags, reactions: reactionsAttribute, accountPeer: item.associatedData.accountPeer, message: item.message, alignment: .left, constrainedWidth: constrainedSize.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, type: item.message.effectivelyIncoming(item.context.account.peerId) ? .incoming : .outgoing) return (layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right + buttonsUpdate.proposedWidth, { boundingWidth in var boundingSize = CGSize() @@ -593,6 +604,7 @@ public final class ChatMessageReactionButtonsNode: ASDisplayNode { public let presentationData: ChatPresentationData public let presentationContext: ChatPresentationContext public let availableReactions: AvailableReactions? + public let savedMessageTags: SavedMessageTags? public let reactions: ReactionsMessageAttribute public let message: Message public let accountPeer: EnginePeer? @@ -604,6 +616,7 @@ public final class ChatMessageReactionButtonsNode: ASDisplayNode { presentationData: ChatPresentationData, presentationContext: ChatPresentationContext, availableReactions: AvailableReactions?, + savedMessageTags: SavedMessageTags?, reactions: ReactionsMessageAttribute, message: Message, accountPeer: EnginePeer?, @@ -614,6 +627,7 @@ public final class ChatMessageReactionButtonsNode: ASDisplayNode { self.presentationData = presentationData self.presentationContext = presentationContext self.availableReactions = availableReactions + self.savedMessageTags = savedMessageTags self.reactions = reactions self.message = message self.accountPeer = accountPeer @@ -652,6 +666,7 @@ public final class ChatMessageReactionButtonsNode: ASDisplayNode { presentationData: arguments.presentationData, presentationContext: arguments.presentationContext, availableReactions: arguments.availableReactions, + savedMessageTags: arguments.savedMessageTags, reactions: arguments.reactions, accountPeer: arguments.accountPeer, message: arguments.message, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift index c97d3af588..92c1750e36 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -129,6 +129,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod layoutInput: .trailingContent(contentWidth: textLayout.trailingLineWidth, reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions), preferAdditionalInset: false)), constrainedSize: textConstrainedSize, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 77f5d14947..e9f61f16e9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -625,6 +625,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { layoutInput: .standalone(reactionSettings: shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) ? ChatMessageDateAndStatusNode.StandaloneReactionSettings() : nil), constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, @@ -838,6 +839,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { presentationData: item.presentationData, presentationContext: item.controllerInteraction.presentationContext, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: reactions, message: item.message, accountPeer: item.associatedData.accountPeer, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 7c2ce808ab..d882a6f43d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -570,6 +570,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { layoutInput: dateLayoutInput, constrainedSize: textConstrainedSize, availableReactions: item.associatedData.availableReactions, + savedMessageTags: item.associatedData.savedMessageTags, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index e5b988c545..d37a02c53d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -134,7 +134,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.titleUpdated(title: new) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAbout(prev, new): var peers = SimpleDictionary() var author: Peer? @@ -165,14 +165,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: let peers = SimpleDictionary() let attributes: [MessageAttribute] = [] let prevMessage = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: prev, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) } case let .changeUsername(prev, new): var peers = SimpleDictionary() @@ -203,7 +203,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var previousAttributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = [] @@ -222,7 +222,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let prevMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) } case let .changeUsernames(prev, new): var peers = SimpleDictionary() @@ -253,7 +253,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var previousAttributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = [] @@ -291,7 +291,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let prevMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) } case let .changePhoto(_, new): var peers = SimpleDictionary() @@ -310,7 +310,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.photoUpdated(image: photo) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleInvites(value): var peers = SimpleDictionary() var author: Peer? @@ -337,7 +337,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleSignatures(value): var peers = SimpleDictionary() var author: Peer? @@ -364,7 +364,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updatePinned(message): switch self.id.contentIndex { case .header: @@ -395,7 +395,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: if let message = message { var peers = SimpleDictionary() @@ -413,7 +413,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } else { var peers = SimpleDictionary() var author: Peer? @@ -435,7 +435,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } case let .editMessage(prev, message): @@ -480,7 +480,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -497,7 +497,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) } case let .deleteMessage(message): switch self.id.contentIndex { @@ -523,7 +523,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -550,7 +550,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { peers[peer.id] = peer } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case .participantJoin, .participantLeave: var peers = SimpleDictionary() @@ -568,7 +568,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { action = TelegramMediaActionType.removedMembers(peerIds: [self.entry.event.peerId]) } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantInvite(participant): var peers = SimpleDictionary() var author: Peer? @@ -585,7 +585,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action: TelegramMediaActionType action = TelegramMediaActionType.addedMembers(peerIds: [participant.peer.id]) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleBan(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -716,7 +716,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleAdmin(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -953,7 +953,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeStickerPack(_, new): var peers = SimpleDictionary() var author: Peer? @@ -982,7 +982,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .togglePreHistoryHidden(value): var peers = SimpleDictionary() var author: Peer? @@ -1012,7 +1012,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateDefaultBannedRights(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -1071,7 +1071,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pollStopped(message): switch self.id.contentIndex { case .header: @@ -1099,7 +1099,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -1116,7 +1116,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.author, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) } case let .linkedPeerUpdated(previous, updated): var peers = SimpleDictionary() @@ -1172,7 +1172,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeGeoLocation(_, updated): var peers = SimpleDictionary() var author: Peer? @@ -1194,12 +1194,12 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let mediaMap = TelegramMediaMap(latitude: updated.latitude, longitude: updated.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } else { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .updateSlowmode(_, newValue): var peers = SimpleDictionary() @@ -1230,7 +1230,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .startGroupCall, .endGroupCall: var peers = SimpleDictionary() var author: Peer? @@ -1267,7 +1267,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantMuteStatus(participantId, isMuted): var peers = SimpleDictionary() var author: Peer? @@ -1301,7 +1301,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateGroupCallSettings(joinMuted): var peers = SimpleDictionary() var author: Peer? @@ -1330,7 +1330,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantVolume(participantId, volume): var peers = SimpleDictionary() var author: Peer? @@ -1361,7 +1361,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteExportedInvitation(invite): var peers = SimpleDictionary() var author: Peer? @@ -1387,7 +1387,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .revokeExportedInvitation(invite): var peers = SimpleDictionary() var author: Peer? @@ -1413,7 +1413,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .editExportedInvitation(_, updatedInvite): var peers = SimpleDictionary() var author: Peer? @@ -1439,7 +1439,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinedViaInvite(invite, joinedViaFolderLink): var peers = SimpleDictionary() var author: Peer? @@ -1470,7 +1470,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeHistoryTTL(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1501,7 +1501,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAvailableReactions(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1572,7 +1572,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeTheme(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1603,7 +1603,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinByRequest(invite, approvedBy): var peers = SimpleDictionary() var author: Peer? @@ -1643,7 +1643,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleCopyProtection(value): var peers = SimpleDictionary() var author: Peer? @@ -1670,7 +1670,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .sendMessage(message): switch self.id.contentIndex { case .header: @@ -1695,7 +1695,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -1712,7 +1712,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .createTopic(info): var peers = SimpleDictionary() @@ -1732,7 +1732,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { }, to: &text, entities: &entities) let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteTopic(info): var peers = SimpleDictionary() var author: Peer? @@ -1753,7 +1753,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .editTopic(prevInfo, newInfo): var peers = SimpleDictionary() var author: Peer? @@ -1826,7 +1826,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pinTopic(prevInfo, newInfo): var peers = SimpleDictionary() var author: Peer? @@ -1864,7 +1864,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleForum(isForum): var peers = SimpleDictionary() var author: Peer? @@ -1885,7 +1885,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleAntiSpam(isEnabled): var peers = SimpleDictionary() var author: Peer? @@ -1906,7 +1906,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeNameColor(_, _, updatedColor, updatedIcon): var peers = SimpleDictionary() var author: Peer? @@ -1975,7 +1975,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: TelegramMediaActionType.CustomTextAttributes(attributes: additionalAttributes)) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeProfileColor(_, _, updatedColor, updatedIcon): var peers = SimpleDictionary() var author: Peer? @@ -2054,7 +2054,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: TelegramMediaActionType.CustomTextAttributes(attributes: additionalAttributes)) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeStatus(_, status): var peers = SimpleDictionary() var author: Peer? @@ -2089,7 +2089,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeWallpaper(_, wallpaper): var peers = SimpleDictionary() var author: Peer? @@ -2117,7 +2117,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatShareMessageTagView/Sources/ChatShareMessageTagView.swift b/submodules/TelegramUI/Components/Chat/ChatShareMessageTagView/Sources/ChatShareMessageTagView.swift index 0dd5418766..547d638c05 100644 --- a/submodules/TelegramUI/Components/Chat/ChatShareMessageTagView/Sources/ChatShareMessageTagView.swift +++ b/submodules/TelegramUI/Components/Chat/ChatShareMessageTagView/Sources/ChatShareMessageTagView.swift @@ -148,6 +148,7 @@ public final class ChatShareMessageTagView: UIView, UndoOverlayControllerAdditio transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: params.size)) reactionContextNode.updateLayout(size: params.size, insets: UIEdgeInsets(), anchorRect: reactionsAnchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: transition) + reactionContextNode.updateIsIntersectingContent(isIntersectingContent: true, transition: .immediate) if isFirstTime { reactionContextNode.animateIn(from: reactionsAnchorRect) } diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 2674eb490d..39c7d4f6c0 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -197,6 +197,33 @@ private func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark } public final class InlineStickerItemLayer: MultiAnimationRenderTarget { + private final class Arguments { + let context: InlineStickerItemLayer.Context + let userLocation: MediaResourceUserLocation + let emoji: ChatTextInputTextCustomEmojiAttribute + let cache: AnimationCache + let renderer: MultiAnimationRenderer + let unique: Bool + let placeholderColor: UIColor + let loopCount: Int? + + let pointSize: CGSize + let pixelSize: CGSize + + init(context: InlineStickerItemLayer.Context, userLocation: MediaResourceUserLocation, emoji: ChatTextInputTextCustomEmojiAttribute, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool, placeholderColor: UIColor, loopCount: Int?, pointSize: CGSize, pixelSize: CGSize) { + self.context = context + self.userLocation = userLocation + self.emoji = emoji + self.cache = cache + self.renderer = renderer + self.unique = unique + self.placeholderColor = placeholderColor + self.loopCount = loopCount + self.pointSize = pointSize + self.pixelSize = pixelSize + } + } + public enum Context: Equatable { public final class Custom: Equatable { public let postbox: Postbox @@ -277,17 +304,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } } - private let context: InlineStickerItemLayer.Context - private let userLocation: MediaResourceUserLocation - private let emoji: ChatTextInputTextCustomEmojiAttribute - private let cache: AnimationCache - private let renderer: MultiAnimationRenderer - private let unique: Bool - private let placeholderColor: UIColor - private let loopCount: Int? - - private let pointSize: CGSize - private let pixelSize: CGSize + private let arguments: Arguments? private var isDisplayingPlaceholder: Bool = false private var didProcessTintColor: Bool = false @@ -353,19 +370,22 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } public init(context: InlineStickerItemLayer.Context, userLocation: MediaResourceUserLocation, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, dynamicColor: UIColor? = nil, loopCount: Int? = nil) { - self.context = context - self.userLocation = userLocation - self.emoji = emoji - self.cache = cache - self.renderer = renderer - self.unique = unique - self.placeholderColor = placeholderColor - self._dynamicColor = dynamicColor - self.loopCount = loopCount - let scale = min(2.0, UIScreenScale) - self.pointSize = pointSize - self.pixelSize = CGSize(width: self.pointSize.width * scale, height: self.pointSize.height * scale) + + self.arguments = Arguments( + context: context, + userLocation: userLocation, + emoji: emoji, + cache: cache, + renderer: renderer, + unique: unique, + placeholderColor: placeholderColor, + loopCount: loopCount, + pointSize: pointSize, + pixelSize: CGSize(width: pointSize.width * scale, height: pointSize.height * scale) + ) + + self._dynamicColor = dynamicColor super.init() @@ -392,7 +412,9 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } override public init(layer: Any) { - preconditionFailure() + self.arguments = nil + + super.init(layer: layer) } required public init?(coder: NSCoder) { @@ -479,9 +501,13 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } private func updatePlayback() { + guard let arguments = self.arguments else { + return + } + var shouldBePlaying = self.isInHierarchyValue && self.isVisibleForAnimations - if shouldBePlaying, let loopCount = self.loopCount, self.currentLoopCount >= loopCount { + if shouldBePlaying, let loopCount = arguments.loopCount, self.currentLoopCount >= loopCount { shouldBePlaying = false } @@ -549,6 +575,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } private func updateFile(file: TelegramMediaFile, attemptSynchronousLoad: Bool) { + guard let arguments = self.arguments else { + return + } + if self.file?.fileId == file.fileId { return } @@ -556,8 +586,8 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.file = file if attemptSynchronousLoad { - if !self.renderer.loadFirstFrameSynchronously(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize) { - if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: self.pointSize, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: self.placeholderColor) { + if !arguments.renderer.loadFirstFrameSynchronously(target: self, cache: arguments.cache, itemId: file.resource.id.stringRepresentation, size: arguments.pixelSize) { + if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: arguments.pointSize, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: arguments.placeholderColor) { self.contents = image.cgImage self.isDisplayingPlaceholder = true self.updateTintColor() @@ -570,10 +600,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } else { let isTemplate = file.isCustomTemplateEmoji - let pointSize = self.pointSize - let placeholderColor = self.placeholderColor + let pointSize = arguments.pointSize + let placeholderColor = arguments.placeholderColor let isThumbnailCancelled = Atomic(value: false) - self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(postbox: self.context.postbox, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true, customColor: isTemplate ? .white : nil), completion: { [weak self] result, isFinal in + self.loadDisposable = arguments.renderer.loadFirstFrame(target: self, cache: arguments.cache, itemId: file.resource.id.stringRepresentation, size: arguments.pixelSize, fetch: animationCacheFetchFile(postbox: arguments.context.postbox, userLocation: arguments.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true, customColor: isTemplate ? .white : nil), completion: { [weak self] result, isFinal in if !result { MultiAnimationRendererImpl.firstFrameQueue.async { let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: pointSize, scale: min(2.0, UIScreenScale), imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor) @@ -605,19 +635,23 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } private func loadAnimation() { + guard let arguments = self.arguments else { + return + } + guard let file = self.file else { return } let isTemplate = file.isCustomTemplateEmoji - let context = self.context + let context = arguments.context if file.isAnimatedSticker || file.isVideoSticker || file.isVideoEmoji { - let keyframeOnly = self.pixelSize.width >= 120.0 + let keyframeOnly = arguments.pixelSize.width >= 120.0 - self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(postbox: self.context.postbox, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil)) + self.disposable = arguments.renderer.add(target: self, cache: arguments.cache, itemId: file.resource.id.stringRepresentation, unique: arguments.unique, size: arguments.pixelSize, fetch: animationCacheFetchFile(postbox: arguments.context.postbox, userLocation: arguments.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil)) } else { - self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: { options in + self.disposable = arguments.renderer.add(target: self, cache: arguments.cache, itemId: file.resource.id.stringRepresentation, unique: arguments.unique, size: arguments.pixelSize, fetch: { options in let dataDisposable = context.postbox.mediaBox.resourceData(file.resource).start(next: { result in guard result.complete else { return @@ -626,7 +660,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { cacheStillSticker(path: result.path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, customColor: isTemplate ? .white : nil) }) - let fetchDisposable = freeMediaFileResourceInteractiveFetched(postbox: context.postbox, userLocation: self.userLocation, fileReference: .customEmoji(media: file), resource: file.resource).start() + let fetchDisposable = freeMediaFileResourceInteractiveFetched(postbox: context.postbox, userLocation: arguments.userLocation, fileReference: .customEmoji(media: file), resource: file.resource).start() return ActionDisposable { dataDisposable.dispose() @@ -645,6 +679,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } override public func transitionToContents(_ contents: AnyObject, didLoop: Bool) { + guard let arguments = self.arguments else { + return + } + if self.isDisplayingPlaceholder { self.isDisplayingPlaceholder = false self.updateTintColor() @@ -675,7 +713,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { if didLoop { self.currentLoopCount += 1 - if let loopCount = self.loopCount, self.currentLoopCount >= loopCount { + if let loopCount = arguments.loopCount, self.currentLoopCount >= loopCount { self.updatePlayback() } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index ad0b9b11d1..32d33aeb28 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -278,7 +278,12 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI chatController.canReadHistory.set(false) let source: ContextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: parentController.navigationController as? NavigationController)) - let contextController = ContextController(presentationData: self.presentationData, source: source, items: savedMessagesPeerMenuItems(context: self.context, threadId: threadId, parentController: parentController) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: source, items: savedMessagesPeerMenuItems(context: self.context, threadId: threadId, parentController: parentController, deletePeerChat: { [weak self] peerId in + guard let self else { + return + } + self.chatListNode.deletePeerChat?(peerId, false) + }) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) parentController.presentInGlobalOverlay(contextController) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 1cb5d5a45f..03bbb12e0d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -298,10 +298,7 @@ public func hasAvailablePeerInfoMediaPanes(context: AccountContext, peerId: Peer let hasSavedMessagesChats: Signal if peerId == context.account.peerId { - hasSavedMessagesChats = context.engine.messages.savedMessagesPeerListHead() - |> map { headPeerId -> Bool in - return headPeerId != nil - } + hasSavedMessagesChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved() |> distinctUntilChanged } else { hasSavedMessagesChats = .single(false) @@ -842,11 +839,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } |> distinctUntilChanged - hasSavedMessagesChats = context.engine.messages.savedMessagesPeerListHead() - |> map { headPeerId -> Bool in - return headPeerId != nil + if peerId == context.account.peerId { + hasSavedMessagesChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved() + } else { + hasSavedMessagesChats = context.engine.messages.savedMessagesPeerListHead() + |> map { headPeerId -> Bool in + return headPeerId != nil + } + |> distinctUntilChanged } - |> distinctUntilChanged } else { hasSavedMessages = .single(false) hasSavedMessagesChats = .single(false) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 694e57159d..e3c27064bc 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -427,6 +427,8 @@ private final class StoryContainerScreenComponent: Component { private var previousSeekTime: Double? private var initialSeekTimestamp: Double? + private var isUpdating: Bool = false + override init(frame: CGRect) { self.backgroundLayer = SimpleLayer() self.backgroundLayer.backgroundColor = UIColor.black.cgColor @@ -486,7 +488,9 @@ private final class StoryContainerScreenComponent: Component { if point != nil { if !self.isHoldingTouch { self.isHoldingTouch = true - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } else { DispatchQueue.main.async { [weak self] in @@ -496,7 +500,9 @@ private final class StoryContainerScreenComponent: Component { if self.isHoldingTouch { self.isHoldingTouch = false - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } } @@ -606,14 +612,18 @@ private final class StoryContainerScreenComponent: Component { } } self.itemSetPinchState = StoryItemSetContainerComponent.PinchState(scale: scale, location: pinchLocation, offset: offset) - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } pinchRecognizer.ended = { [weak self] in guard let self else { return } self.itemSetPinchState = nil - self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) + } } self.addGestureRecognizer(pinchRecognizer) @@ -677,7 +687,9 @@ private final class StoryContainerScreenComponent: Component { } } - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } }) @@ -764,12 +776,16 @@ private final class StoryContainerScreenComponent: Component { if let itemSetPanState = self.itemSetPanState, !itemSetPanState.didBegin { self.itemSetPanState = ItemSetPanState(fraction: 0.0, didBegin: true) if !updateImmediately { - self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + } } } else { self.itemSetPanState = ItemSetPanState(fraction: 0.0, didBegin: true) if !updateImmediately { - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } @@ -801,7 +817,9 @@ private final class StoryContainerScreenComponent: Component { itemSetPanState.fraction = fraction self.itemSetPanState = itemSetPanState - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } @@ -841,7 +859,9 @@ private final class StoryContainerScreenComponent: Component { itemSetPanState.fraction = itemSetPanState.fraction - 1.0 } self.itemSetPanState = itemSetPanState - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } else { shouldDismiss = mayDismiss } @@ -851,14 +871,18 @@ private final class StoryContainerScreenComponent: Component { self.itemSetPanState = itemSetPanState let transition = Transition(animation: .curve(duration: 0.4, curve: .spring)) - self.state?.updated(transition: transition) + if !self.isUpdating { + self.state?.updated(transition: transition) + } transition.attachAnimation(view: self, id: "panState", completion: { [weak self] completed in guard let self, completed else { return } self.itemSetPanState = nil - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } /*if let component = self.component { component.content.resetSideStates() @@ -890,12 +914,16 @@ private final class StoryContainerScreenComponent: Component { case .began: if self.itemSetPanState == nil { self.itemSetPanState = ItemSetPanState(fraction: 0.0, didBegin: false) - self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + } } case .cancelled, .ended: if let itemSetPanState = self.itemSetPanState, !itemSetPanState.didBegin { self.itemSetPanState = nil - self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + if !self.isUpdating { + self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) + } } default: break @@ -977,15 +1005,21 @@ private final class StoryContainerScreenComponent: Component { } self.didAnimateIn = true - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } }) } else { self.didAnimateIn = true - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } else { self.didAnimateIn = true - self.state?.updated(transition: .immediate) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } else { self.layer.allowsGroupOpacity = true @@ -1180,6 +1214,11 @@ private final class StoryContainerScreenComponent: Component { return availableSize } + self.isUpdating = true + defer { + self.isUpdating = false + } + let environment = environment[ViewControllerComponentContainer.Environment.self].value self.environment = environment @@ -1308,9 +1347,9 @@ private final class StoryContainerScreenComponent: Component { if self.stateValue?.slice == nil { self.environment?.controller()?.dismiss() } else { - let startTime = CFAbsoluteTimeGetCurrent() - self.state?.updated(transition: .immediate) - print("update time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } } } else { DispatchQueue.main.async { [weak self] in diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index d9946e3b38..1e78b16722 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -514,10 +514,13 @@ func updateChatPresentationInterfaceStateImpl( selfController.presentationInterfaceStatePromise.set(selfController.presentationInterfaceState) - if let historyFilter = selfController.presentationInterfaceState.historyFilter, historyFilter.isActive, !historyFilter.customTags.isEmpty { - selfController.chatDisplayNode.historyNode.updateTag(tag: .customTag(historyFilter.customTags[0])) + if case .tag = selfController.chatDisplayNode.historyNode.tag { } else { - selfController.chatDisplayNode.historyNode.updateTag(tag: nil) + if let historyFilter = selfController.presentationInterfaceState.historyFilter, historyFilter.isActive, !historyFilter.customTags.isEmpty { + selfController.chatDisplayNode.historyNode.updateTag(tag: .customTag(historyFilter.customTags[0])) + } else { + selfController.chatDisplayNode.historyNode.updateTag(tag: nil) + } } selfController.updateDownButtonVisibility() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 851ffc10a3..fb58b32f96 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5067,9 +5067,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let hasSearchTags: Signal - if case let .peer(peerId) = self.chatLocation, peerId == context.account.peerId { + if let peerId = self.chatLocation.peerId, peerId == context.account.peerId { hasSearchTags = context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId) + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId) ) |> map { tags -> Bool in return !tags.isEmpty @@ -5619,9 +5619,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let hasSearchTags: Signal - if case let .peer(peerId) = self.chatLocation, peerId == context.account.peerId { + if let peerId = self.chatLocation.peerId, peerId == context.account.peerId { hasSearchTags = context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId) + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId) ) |> map { tags -> Bool in return !tags.isEmpty @@ -5727,6 +5727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return $0.updatedPeer { _ in return renderedPeer }.updatedSavedMessagesTopicPeer(savedMessagesPeer?.peer) + .updatedHasSearchTags(hasSearchTags) }) (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: savedMessagesPeer?.peer, overrideImage: imageOverride) diff --git a/submodules/TelegramUI/Sources/ChatControllerForwardMessages.swift b/submodules/TelegramUI/Sources/ChatControllerForwardMessages.swift index db1791d63f..1e8f3ebfdc 100644 --- a/submodules/TelegramUI/Sources/ChatControllerForwardMessages.swift +++ b/submodules/TelegramUI/Sources/ChatControllerForwardMessages.swift @@ -11,6 +11,7 @@ import TextFormat import UndoUI import ChatInterfaceState import PremiumUI +import ReactionSelectionNode extension ChatControllerImpl { func forwardMessages(messageIds: [MessageId], options: ChatInterfaceForwardOptionsState? = nil, resetCurrent: Bool = false) { @@ -110,8 +111,17 @@ extension ChatControllerImpl { }) let commit: ([EnqueueMessage]) -> Void = { result in + var result = result + strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }).updatedSearch(nil) }) + var correlationIds: [Int64] = [] + for i in 0 ..< result.count { + let correlationId = Int64.random(in: Int64.min ... Int64.max) + correlationIds.append(correlationId) + result[i] = result[i].withUpdatedCorrelationId(correlationId) + } + var displayPeers: [EnginePeer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) @@ -174,39 +184,53 @@ extension ChatControllerImpl { } } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if savedMessages, let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) - }) + let reactionItems: Signal<[ReactionItem], NoError> + if savedMessages && messages.count == 1 { + reactionItems = tagMessageReactions(context: strongSelf.context) + } else { + reactionItems = .single([]) + } + + let _ = (reactionItems + |> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] reactionItems in + guard let strongSelf else { + return } - return false - }), in: .current) + + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, position: savedMessages && messages.count == 1 ? .top : .bottom, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) + }) + } + return false + }, additionalView: (savedMessages && messages.count == 1) ? chatShareToSavedMessagesAdditionalView(strongSelf, reactionItems: reactionItems, correlationId: correlationIds.first) : nil), in: .current) + }) } switch mode { - case .generic: - commit(result) - case .silent: - let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: true) - commit(transformedMessages) - case .schedule: - strongSelf.presentScheduleTimePicker(completion: { [weak self] scheduleTime in - if let strongSelf = self { - let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: false, scheduleTime: scheduleTime) - commit(transformedMessages) - } - }) - case .whenOnline: - let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: false, scheduleTime: scheduleWhenOnlineTimestamp) - commit(transformedMessages) + case .generic: + commit(result) + case .silent: + let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: true) + commit(transformedMessages) + case .schedule: + strongSelf.presentScheduleTimePicker(completion: { [weak self] scheduleTime in + if let strongSelf = self { + let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: false, scheduleTime: scheduleTime) + commit(transformedMessages) + } + }) + case .whenOnline: + let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: false, scheduleTime: scheduleWhenOnlineTimestamp) + commit(transformedMessages) } } controller.peerSelected = { [weak self, weak controller] peer, threadId in @@ -243,25 +267,44 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.hapticFeedback.success() } - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in - if case .info = value, let strongSelf = self { - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId)) - |> deliverOnMainQueue).startStandalone(next: { peer in - guard let strongSelf = self, let peer = peer, let navigationController = strongSelf.effectiveNavigationController else { - return - } - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .always, purposefulAction: {}, peekData: nil)) - }) - return true - } - return false - }), in: .current) + let reactionItems: Signal<[ReactionItem], NoError> + if messages.count == 1 { + reactionItems = tagMessageReactions(context: strongSelf.context) + } else { + reactionItems = .single([]) + } - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messages.map { message -> EnqueueMessage in - return .forward(source: message.id, threadId: nil, grouping: .auto, attributes: [], correlationId: nil) + var correlationIds: [Int64] = [] + let mappedMessages = messages.map { message -> EnqueueMessage in + let correlationId = Int64.random(in: Int64.min ... Int64.max) + correlationIds.append(correlationId) + return .forward(source: message.id, threadId: nil, grouping: .auto, attributes: [], correlationId: correlationId) + } + + let _ = (reactionItems + |> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] reactionItems in + guard let strongSelf else { + return + } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, position: messages.count == 1 ? .top : .bottom, animateInAsReplacement: true, action: { [weak self] value in + if case .info = value, let strongSelf = self { + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let strongSelf = self, let peer = peer, let navigationController = strongSelf.effectiveNavigationController else { + return + } + + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .always, purposefulAction: {}, peekData: nil)) + }) + return true + } + return false + }, additionalView: messages.count == 1 ? chatShareToSavedMessagesAdditionalView(strongSelf, reactionItems: reactionItems, correlationId: correlationIds.first) : nil), in: .current) }) + + let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: mappedMessages) |> deliverOnMainQueue).startStandalone(next: { messageIds in if let strongSelf = self { let signals: [Signal] = messageIds.compactMap({ id -> Signal? in diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index 90c759f813..f02e9aae97 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -13,6 +13,7 @@ import TooltipUI import StickerPackPreviewUI import TextNodeWithEntities import ChatPresentationInterfaceState +import PromptUI extension ChatControllerImpl { func openMessageReactionContextMenu(message: Message, sourceView: ContextExtractedContentContainingView, gesture: ContextGesture?, value: MessageReaction.Reaction) { @@ -29,6 +30,8 @@ extension ChatControllerImpl { a(.default) return } + self.chatDisplayNode.historyNode.frozenMessageForScrollingReset = message.id + self.interfaceInteraction?.updateHistoryFilter { _ in return ChatPresentationInterfaceState.HistoryFilter(customTags: tags, isActive: true) } @@ -37,6 +40,39 @@ extension ChatControllerImpl { }))) } + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "Edit Title", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + guard let self else { + a(.default) + return + } + a(.default) + + let _ = (self.context.engine.stickers.savedMessageTagData() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] savedMessageTags in + guard let self, let savedMessageTags else { + return + } + + let reaction = value + + //TODO:localize + let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: "Edit Title", value: savedMessageTags.tags.first(where: { $0.reaction == reaction })?.title ?? "", characterLimit: 10, apply: { [weak self] value in + guard let self else { + return + } + + if let value { + let _ = self.context.engine.stickers.setSavedMessageTagTitle(reaction: reaction, title: value.isEmpty ? nil : value).start() + } + }) + self.interfaceInteraction?.presentController(promptController, nil) + }) + }))) + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_ReactionContextMenu_RemoveTag, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/TagRemove"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] _, a in diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift index e7af78dfc7..1073f6cc60 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift @@ -14,6 +14,62 @@ import ChatQrCodeScreen import ChatShareMessageTagView import ReactionSelectionNode +func chatShareToSavedMessagesAdditionalView(_ chatController: ChatControllerImpl, reactionItems: [ReactionItem], correlationId: Int64?) -> (() -> UndoOverlayControllerAdditionalView?)? { + guard let correlationId else { + return nil + } + return { [weak chatController] () -> UndoOverlayControllerAdditionalView? in + guard let chatController else { + return nil + } + return ChatShareMessageTagView(context: chatController.context, presentationData: chatController.presentationData, reactionItems: reactionItems, completion: { [weak chatController] file, updateReaction in + guard let chatController else { + return + } + + let _ = (chatController.context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: chatController.context.account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 45, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: []) + |> map { view, _, _ -> [EngineMessage.Id] in + guard let messageId = chatController.context.engine.messages.synchronouslyLookupCorrelationId(correlationId: correlationId) else { + return [] + } + + let exactResult = view.entries.compactMap { entry -> EngineMessage.Id? in + if entry.message.id == messageId { + return entry.message.id + } else { + return nil + } + } + if !exactResult.isEmpty { + return exactResult + } + + return [] + } + |> filter { !$0.isEmpty } + |> take(1) + |> timeout(5.0, queue: .mainQueue(), alternate: .single([])) + |> deliverOnMainQueue).start(next: { [weak chatController] messageIds in + guard let chatController else { + return + } + if let messageId = messageIds.first { + let _ = chatController.context.engine.messages.setMessageReactions(id: messageId, reactions: [updateReaction]) + + var isBuiltinReaction = false + if case .builtin = updateReaction { + isBuiltinReaction = true + } + let presentationData = chatController.context.sharedContext.currentPresentationData.with { $0 } + chatController.present(UndoOverlayController(presentationData: presentationData, content: .messageTagged(context: chatController.context, customEmoji: file, isBuiltinReaction: isBuiltinReaction), elevatedLayout: false, position: .top, animateInAsReplacement: true, action: { _ in + return false + }), in: .current) + } + }) + }) + } +} + extension ChatControllerImpl { func openMessageShareMenu(id: EngineMessage.Id) { guard let messages = self.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id), let message = messages.first else { @@ -150,62 +206,7 @@ extension ChatControllerImpl { }) } return false - }, additionalView: savedMessages ? { [weak self] () -> UndoOverlayControllerAdditionalView? in - guard let self else { - return nil - } - return ChatShareMessageTagView(context: self.context, presentationData: self.presentationData, reactionItems: reactionItems, completion: { [weak self] file, updateReaction in - guard let self else { - return - } - - let _ = (self.context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: context.account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 45, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: []) - |> map { view, _, _ -> [EngineMessage.Id] in - //TODO:filter? - for entry in view.entries.reversed() { - if entry.message.id.namespace == Namespaces.Message.Cloud { - return [entry.message.id] - } - } - - return view.entries.compactMap { entry -> EngineMessage.Id? in - for attribute in entry.message.attributes { - if let attribute = attribute as? OutgoingMessageInfoAttribute { - if let correlationId = attribute.correlationId { - if correlationIds.contains(correlationId) { - if entry.message.id.namespace == Namespaces.Message.Cloud { - return entry.message.id - } else { - return nil - } - } - } - } - } - return nil - } - } - |> filter { !$0.isEmpty } - |> take(1) - |> timeout(5.0, queue: .mainQueue(), alternate: .single([])) - |> deliverOnMainQueue).start(next: { [weak self] messageIds in - guard let self else { - return - } - if let messageId = messageIds.first { - let _ = context.engine.messages.setMessageReactions(id: messageId, reactions: [updateReaction]) - - var isBuiltinReaction = false - if case .builtin = updateReaction { - isBuiltinReaction = true - } - self.present(UndoOverlayController(presentationData: presentationData, content: .messageTagged(context: self.context, customEmoji: file, isBuiltinReaction: isBuiltinReaction), elevatedLayout: false, position: .top, animateInAsReplacement: true, action: { _ in - return false - }), in: .current) - } - }) - }) - } : nil), in: .current) + }, additionalView: savedMessages ? chatShareToSavedMessagesAdditionalView(self, reactionItems: reactionItems, correlationId: correlationIds.first) : nil), in: .current) }) }) } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index f41ea99096..699f91880a 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -322,6 +322,7 @@ private func extractAssociatedData( currentlyPlayingMessageId: MessageIndex?, isCopyProtectionEnabled: Bool, availableReactions: AvailableReactions?, + savedMessageTags: SavedMessageTags?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, alwaysDisplayTranscribeButton: ChatMessageItemAssociatedData.DisplayTranscribeButton, @@ -388,7 +389,7 @@ private func extractAssociatedData( automaticDownloadPeerId = message.peerId } - return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers) + return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers) } private extension ChatHistoryLocationInput { @@ -438,7 +439,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto private let chatLocationContextHolder: Atomic private let source: ChatHistoryListSource private let subject: ChatControllerSubject? - private var tag: HistoryViewInputTag? + private(set) var tag: HistoryViewInputTag? private let controllerInteraction: ChatControllerInteraction private let selectedMessages: Signal?, NoError> private let messageTransitionNode: () -> ChatMessageTransitionNodeImpl? @@ -690,6 +691,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto private var allowDustEffect: Bool = true private var dustEffectLayer: DustEffectLayer? + var frozenMessageForScrollingReset: EngineMessage.Id? + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal), chatLocation: ChatLocation, chatLocationContextHolder: Atomic, tag: HistoryViewInputTag?, source: ChatHistoryListSource, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>, mode: ChatHistoryListMode = .bubbles, rotated: Bool = false, messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl?) { var tag = tag if case .pinnedMessages = subject { @@ -1131,10 +1134,39 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let messageTransitionNode = self.messageTransitionNode let mode = self.mode + var resetScrollingMessageId: (index: MessageIndex, offset: CGFloat)? + var resetScrolling = resetScrolling if resetScrolling { - self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Initial(count: historyMessageCount), id: 0) + if let frozenMessageForScrollingReset = self.frozenMessageForScrollingReset { + self.forEachVisibleMessageItemNode { itemNode in + if resetScrollingMessageId != nil { + return + } + if let item = itemNode.item, item.message.id == frozenMessageForScrollingReset { + let distanceToNode = self.insets.top - itemNode.frame.minY + resetScrollingMessageId = (item.message.index, -distanceToNode) + } + } + } + + self.forEachVisibleMessageItemNode { itemNode in + if resetScrollingMessageId != nil { + return + } + if let item = itemNode.item { + let distanceToNode = self.insets.top - itemNode.frame.minY + resetScrollingMessageId = (item.message.index, -distanceToNode) + } + } + + if let resetScrollingMessageId { + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(subject: MessageHistoryScrollToSubject(index: .message(resetScrollingMessageId.index), quote: nil), anchorIndex: .message(resetScrollingMessageId.index), sourceIndex: .message(resetScrollingMessageId.index), scrollPosition: .top(resetScrollingMessageId.offset), animated: false, highlight: false), id: (self.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) + } else { + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Initial(count: historyMessageCount), id: (self.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) + } } + self.frozenMessageForScrollingReset = nil var appendMessagesFromTheSameGroup = false if case .pinnedMessages = subject { @@ -1327,6 +1359,12 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } let availableReactions: Signal = (context as! AccountContextImpl).availableReactions + let savedMessageTags: Signal + if chatLocation.peerId == self.context.account.peerId { + savedMessageTags = context.engine.stickers.savedMessageTagData() + } else { + savedMessageTags = .single(nil) + } let defaultReaction = combineLatest( self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)), @@ -1430,6 +1468,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, + savedMessageTags, defaultReaction, accountPeer, audioTranscriptionSuggestion, @@ -1441,7 +1480,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto audioTranscriptionTrial, chatThemes, deviceContactsNumbers - ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers in + ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers in let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises func applyHole() { @@ -1600,7 +1639,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto translateToLanguage = languageCode } - let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers) + let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers) let filteredEntries = chatHistoryEntriesForView( location: chatLocation, @@ -1832,9 +1871,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto mappedTransition.options.remove(.AnimateTopItemPosition) mappedTransition.options.remove(.RequestItemInsertionAnimations) } - if forceSynchronous { + if forceSynchronous || resetScrolling { mappedTransition.options.insert(.Synchronous) } + if resetScrolling { + mappedTransition.options.insert(.AnimateAlpha) + mappedTransition.options.insert(.AnimateFullTransition) + } if resetScrolling { resetScrolling = false @@ -3156,7 +3199,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let animated = transition.options.contains(.AnimateInsertion) var previousCloneView: UIView? - if transition.animateFromPreviousFilter { + if transition.animateFromPreviousFilter, !"".isEmpty { previousCloneView = self.view.snapshotView(afterScreenUpdates: false) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index f19abefb67..d961ac3504 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -156,13 +156,16 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState return (panel, nil) } } else { - if let currentPanel = (currentPanel as? ChatChannelSubscriberInputPanelNode) ?? (currentSecondaryPanel as? ChatChannelSubscriberInputPanelNode) { - return (currentPanel, nil) + if message.threadId == context.account.peerId.toInt64() { } else { - let panel = ChatChannelSubscriberInputPanelNode() - panel.interfaceInteraction = interfaceInteraction - panel.context = context - return (panel, nil) + if let currentPanel = (currentPanel as? ChatChannelSubscriberInputPanelNode) ?? (currentSecondaryPanel as? ChatChannelSubscriberInputPanelNode) { + return (currentPanel, nil) + } else { + let panel = ChatChannelSubscriberInputPanelNode() + panel.interfaceInteraction = interfaceInteraction + panel.context = context + return (panel, nil) + } } } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 6f587c3f06..f1216d96a2 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -13,11 +13,20 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat return nil } if let search = chatPresentationInterfaceState.search, chatPresentationInterfaceState.hasSearchTags { - if chatPresentationInterfaceState.chatLocation.peerId == context.account.peerId, case .everything = search.domain { + var matches = false + if chatPresentationInterfaceState.chatLocation.peerId == context.account.peerId { + if case .everything = search.domain { + matches = true + } else if case .tag = search.domain, search.query.isEmpty { + matches = true + } + } + + if matches { if let currentPanel = currentPanel as? ChatSearchTitleAccessoryPanelNode { return currentPanel } else { - let panel = ChatSearchTitleAccessoryPanelNode(context: context) + let panel = ChatSearchTitleAccessoryPanelNode(context: context, chatLocation: chatPresentationInterfaceState.chatLocation) panel.interfaceInteraction = interfaceInteraction return panel } diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index 5773890e3a..0635fbd6b7 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -12,6 +12,8 @@ import UIKitRuntimeUtils import TelegramCore import EmojiStatusComponent import SwiftSignalKit +import ContextUI +import PromptUI final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UIScrollViewDelegate { private struct Params: Equatable { @@ -47,39 +49,56 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc private final class Item { let reaction: MessageReaction.Reaction let count: Int + let title: String? let file: TelegramMediaFile - init(reaction: MessageReaction.Reaction, count: Int, file: TelegramMediaFile) { + init(reaction: MessageReaction.Reaction, count: Int, title: String?, file: TelegramMediaFile) { self.reaction = reaction self.count = count + self.title = title self.file = file } } - private final class ItemView: HighlightTrackingButton { + private final class ItemView: UIView { private let context: AccountContext private let action: () -> Void + private let extractedContainerNode: ContextExtractedContentContainingNode + private let containerNode: ContextControllerSourceNode + + private let containerButton: HighlightTrackingButton + private let background: UIImageView private let icon = ComponentView() private let counter = ComponentView() - init(context: AccountContext, action: @escaping (() -> Void)) { + init(context: AccountContext, action: @escaping (() -> Void), contextGesture: @escaping (ContextGesture, ContextExtractedContentContainingNode) -> Void) { + self.context = context + self.action = action + + self.extractedContainerNode = ContextExtractedContentContainingNode() + self.containerNode = ContextControllerSourceNode() + + self.containerButton = HighlightTrackingButton() + self.background = UIImageView() if let image = UIImage(bundleImageName: "Chat/Title Panels/SearchTagTab") { self.background.image = image.stretchableImage(withLeftCapWidth: 8, topCapHeight: 0).withRenderingMode(.alwaysTemplate) } - self.context = context - self.action = action - super.init(frame: CGRect()) - self.addSubview(self.background) + self.extractedContainerNode.contentNode.view.addSubview(self.containerButton) - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.containerNode.addSubnode(self.extractedContainerNode) + self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode + self.addSubview(self.containerNode.view) - self.highligthedChanged = { [weak self] highlighted in + self.containerButton.addSubview(self.background) + + self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.containerButton.highligthedChanged = { [weak self] highlighted in if let self, self.bounds.width > 0.0 { let topScale: CGFloat = (self.bounds.width - 1.0) / self.bounds.width let maxScale: CGFloat = (self.bounds.width + 1.0) / self.bounds.width @@ -103,6 +122,13 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc } } } + + self.containerNode.activated = { [weak self] gesture, _ in + guard let self else { + return + } + contextGesture(gesture, self.extractedContainerNode) + } } required init?(coder: NSCoder) { @@ -152,10 +178,16 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc containerSize: reactionDisplaySize ) + let title: String + if let value = item.title, !value.isEmpty { + title = "\(value) \(item.count)" + } else { + title = "\(item.count)" + } let counterSize = self.counter.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "\(item.count)", font: Font.regular(14.0), textColor: isSelected ? theme.list.itemCheckColors.foregroundColor : theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.6))) + text: .plain(NSAttributedString(string: title, font: Font.regular(14.0), textColor: isSelected ? theme.list.itemCheckColors.foregroundColor : theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.6))) )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) @@ -169,7 +201,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc if let iconView = self.icon.view { if iconView.superview == nil { iconView.isUserInteractionEnabled = false - self.addSubview(iconView) + self.containerButton.addSubview(iconView) } iconView.frame = reactionDisplaySize.centered(around: iconFrame.center) } @@ -177,17 +209,28 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc if let counterView = self.counter.view { if counterView.superview == nil { counterView.isUserInteractionEnabled = false - self.addSubview(counterView) + self.containerButton.addSubview(counterView) } counterView.frame = counterFrame } - self.background.tintColor = isSelected ? theme.list.itemCheckColors.fillColor : theme.list.controlSecondaryColor + if theme.overallDarkAppearance { + self.background.tintColor = isSelected ? theme.list.itemCheckColors.fillColor : UIColor(white: 1.0, alpha: 0.1) + } else { + self.background.tintColor = isSelected ? theme.list.itemCheckColors.fillColor : theme.list.controlSecondaryColor + } if let image = self.background.image { let backgroundFrame = CGRect(origin: CGPoint(x: -6.0, y: floorToScreenPixels((size.height - image.size.height) * 0.5)), size: CGSize(width: size.width + 6.0 + 9.0, height: image.size.height)) transition.setFrame(view: self.background, frame: backgroundFrame) } + transition.setFrame(view: self.containerButton, frame: CGRect(origin: CGPoint(), size: size)) + + self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size) + self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) + self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + return size } } @@ -213,7 +256,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc private var itemsDisposable: Disposable? - init(context: AccountContext) { + init(context: AccountContext, chatLocation: ChatLocation) { self.context = context self.scrollView = ScrollView(frame: CGRect()) @@ -239,7 +282,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc self.scrollView.disablesInteractiveTransitionGestureRecognizer = true let tagsAndFiles: Signal<([MessageReaction.Reaction: Int], [Int64: TelegramMediaFile]), NoError> = context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId) + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: chatLocation.threadId) ) |> distinctUntilChanged |> mapToSignal { tags -> Signal<([MessageReaction.Reaction: Int], [Int64: TelegramMediaFile]), NoError> in @@ -262,9 +305,10 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc var isFirstUpdate = true self.itemsDisposable = (combineLatest( context.engine.stickers.availableReactions(), + context.engine.stickers.savedMessageTagData(), tagsAndFiles ) - |> deliverOnMainQueue).start(next: { [weak self] availableReactions, tagsAndFiles in + |> deliverOnMainQueue).start(next: { [weak self] availableReactions, savedMessageTags, tagsAndFiles in guard let self else { return } @@ -272,13 +316,15 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc let (tags, files) = tagsAndFiles for (reaction, count) in tags { + let title = savedMessageTags?.tags.first(where: { $0.reaction == reaction })?.title + switch reaction { case .builtin: if let availableReactions { inner: for availableReaction in availableReactions.reactions { if availableReaction.value == reaction { if let file = availableReaction.centerAnimation { - self.items.append(Item(reaction: reaction, count: count, file: file)) + self.items.append(Item(reaction: reaction, count: count, title: title, file: file)) } break inner } @@ -286,7 +332,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc } case let .custom(fileId): if let file = files[fileId] { - self.items.append(Item(reaction: reaction, count: count, file: file)) + self.items.append(Item(reaction: reaction, count: count, title: title, file: file)) } } } @@ -376,6 +422,34 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc if let itemView = self.itemViews[reaction] { self.scrollView.scrollRectToVisible(itemView.frame.insetBy(dx: -46.0, dy: 0.0), animated: true) } + }, contextGesture: { [weak self] gesture, sourceNode in + guard let self, let interfaceInteraction = self.interfaceInteraction, let chatController = interfaceInteraction.chatController() else { + gesture.cancel() + return + } + + var items: [ContextMenuItem] = [] + + let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "Edit Title", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, a in + guard let self else { + a(.default) + return + } + + c.dismiss(completion: { [weak self] in + guard let self else { + return + } + self.openEditTagTitle(reaction: reaction) + }) + }))) + + let controller = ContextController(presentationData: presentationData, source: .extracted(TagContextExtractedContentSource(controller: chatController, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + interfaceInteraction.presentGlobalOverlayController(controller, nil) }) self.itemViews[itemId] = itemView self.scrollView.addSubview(itemView) @@ -429,4 +503,50 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc self.scrollView.contentSize = contentSize } } + + private func openEditTagTitle(reaction: MessageReaction.Reaction) { + let _ = (self.context.engine.stickers.savedMessageTagData() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] savedMessageTags in + guard let self, let savedMessageTags else { + return + } + + //TODO:localize + let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: "Edit Title", value: savedMessageTags.tags.first(where: { $0.reaction == reaction })?.title ?? "", characterLimit: 10, apply: { [weak self] value in + guard let self else { + return + } + + if let value { + let _ = self.context.engine.stickers.setSavedMessageTagTitle(reaction: reaction, title: value.isEmpty ? nil : value).start() + } + }) + self.interfaceInteraction?.presentController(promptController, nil) + }) + } +} + +private final class TagContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool + let ignoreContentTouches: Bool = true + let blurBackground: Bool = true + let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center + + private let controller: ViewController + private let sourceNode: ContextExtractedContentContainingNode + + init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool) { + self.controller = controller + self.sourceNode = sourceNode + self.keepInPlace = keepInPlace + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 9d81d83f16..efa1dc4316 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1796,7 +1796,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } else { if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) { placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder - } else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation, !replyThreadMessage.isForumPost { + } else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation, !replyThreadMessage.isForumPost, replyThreadMessage.peerId != self.context?.account.peerId { if replyThreadMessage.isChannelPost { placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment } else { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 603c2ecdaa..9e059bb804 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1771,7 +1771,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { chatLocation = .peer(id: messages.first!.id.peerId) } - return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: isPreview), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil, isPremium: false, accountPeer: accountPeer.flatMap(EnginePeer.init), forceInlineReactions: true, isStandalone: isStandalone), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) + return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: isPreview), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: accountPeer.flatMap(EnginePeer.init), forceInlineReactions: true, isStandalone: isStandalone), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) } public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {