From 16faaa457564cff6d6d6688cd5d5132693c6475a Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 3 May 2024 22:56:50 +0400 Subject: [PATCH] [WIP] Send message effects --- .../Sources/AccountContext.swift | 1 + .../Sources/ChatController.swift | 3 + .../Sources/AttachmentPanel.swift | 7 +- .../Sources/CallListController.swift | 2 +- .../ChatListUI/Sources/ChatContextMenus.swift | 15 +- .../Sources/ChatListController.swift | 30 +- .../Sources/ChatListSearchContainerNode.swift | 16 +- submodules/ChatSendMessageActionUI/BUILD | 8 + ...ChatSendMessageActionSheetController.swift | 119 ++- ...SendMessageActionSheetControllerNode.swift | 231 +++-- .../ChatSendMessageContextScreen.swift | 860 ++++++++++++++++++ .../Sources/MessageItemView.swift | 318 +++++++ .../Sources/ContactContextMenus.swift | 2 +- .../Sources/ContactsController.swift | 4 +- .../ContextUI/Sources/ContextController.swift | 6 +- .../ContextControllerActionsStackNode.swift | 58 +- .../Items/UniversalVideoGalleryItem.swift | 8 +- .../Sources/ReactionContextNode.swift | 36 +- .../Source/Signal_Combine.swift | 6 + .../Sources/ThemePickerController.swift | 18 +- .../Themes/ThemeSettingsController.swift | 18 +- .../Sources/ChannelStatsController.swift | 2 +- .../Sources/MessageStatsController.swift | 2 +- .../Sources/StickerPackScreen.swift | 6 +- submodules/TelegramApi/Sources/Api0.swift | 13 +- submodules/TelegramApi/Sources/Api1.swift | 96 +- submodules/TelegramApi/Sources/Api13.swift | 18 +- submodules/TelegramApi/Sources/Api2.swift | 90 +- submodules/TelegramApi/Sources/Api24.swift | 24 +- submodules/TelegramApi/Sources/Api29.swift | 110 ++- submodules/TelegramApi/Sources/Api3.swift | 50 + submodules/TelegramApi/Sources/Api30.swift | 90 +- submodules/TelegramApi/Sources/Api31.swift | 48 + submodules/TelegramApi/Sources/Api35.swift | 44 +- .../Sources/VoiceChatController.swift | 14 +- .../Sources/Account/AccountManager.swift | 1 + .../ApiUtils/StoreMessage_Telegram.swift | 12 +- .../TelegramCore/Sources/Authorization.swift | 2 +- .../PendingMessages/EnqueueMessage.swift | 2 + .../StandaloneSendMessage.swift | 8 +- .../State/AccountStateManagementUtils.swift | 2 +- .../Sources/State/AccountTaskManager.swift | 1 + .../Sources/State/ApplyUpdateMessage.swift | 2 +- .../State/AvailableMessageEffects.swift | 312 +++++++ .../Sources/State/PendingMessageManager.swift | 30 +- .../Sources/State/Serialization.swift | 2 +- .../Sources/State/UpdateMessageService.swift | 4 +- .../Sources/State/UpdatesApiUtils.swift | 12 +- .../SyncCore_EffectMessageAttribute.swift | 18 + .../SyncCore/SyncCore_Namespaces.swift | 1 + .../Stickers/TelegramEngineStickers.swift | 4 + .../ChatMessageAnimatedStickerItemNode.swift | 1 + .../ChatMessageAttachedContentNode.swift | 19 + .../ChatMessageBubbleContentNode.swift | 4 + .../Chat/ChatMessageBubbleItemNode/BUILD | 3 + .../Sources/ChatMessageBubbleItemNode.swift | 209 +++++ .../ChatMessageContactBubbleContentNode.swift | 8 + .../ChatMessageDateAndStatusNode.swift | 198 +--- .../ChatMessageFileBubbleContentNode.swift | 7 + .../ChatMessageGameBubbleContentNode.swift | 7 + ...ChatMessageGiveawayBubbleContentNode.swift | 8 + ...MessageInstantVideoBubbleContentNode.swift | 7 + .../ChatMessageInteractiveFileNode.swift | 1 + ...atMessageInteractiveInstantVideoNode.swift | 1 + .../ChatMessageInteractiveMediaNode.swift | 1 + .../ChatMessageInvoiceBubbleContentNode.swift | 7 + .../ChatMessageMapBubbleContentNode.swift | 8 + .../ChatMessageMediaBubbleContentNode.swift | 7 + .../ChatMessagePollBubbleContentNode.swift | 8 + ...atMessageRestrictedBubbleContentNode.swift | 1 + .../Sources/ChatMessageStickerItemNode.swift | 1 + .../ChatMessageTextBubbleContentNode.swift | 8 + .../ChatMessageWebpageBubbleContentNode.swift | 4 + .../ChatRecentActionsControllerNode.swift | 2 +- .../ChatRecentActionsHistoryTransition.swift | 118 +-- .../Sources/TopMessageReactions.swift | 41 +- .../Sources/ChatControllerInteraction.swift | 12 +- .../Sources/EmojiPagerContentSignals.swift | 136 +++ .../Sources/MediaEditorScreen.swift | 4 +- .../Sources/Panes/PeerInfoMembersPane.swift | 6 +- .../Sources/PeerInfoScreen.swift | 112 +-- .../Sources/PeerInfoStoryPaneNode.swift | 6 +- .../Sources/PeerReportScreen.swift | 2 +- .../Sources/PeerSelectionControllerNode.swift | 7 +- .../Sources/BusinessLinksSetupScreen.swift | 4 +- .../Sources/StorageUsageScreen.swift | 18 +- .../StoryItemSetContainerComponent.swift | 14 +- .../TelegramUI/Sources/AccountContext.swift | 13 + .../Chat/ChatControllerLoadDisplayNode.swift | 8 +- .../Chat/ChatControllerMediaRecording.swift | 2 +- .../Chat/ChatMessageActionOptions.swift | 4 +- ...ChatMessageDisplaySendMessageOptions.swift | 20 +- .../TelegramUI/Sources/ChatController.swift | 10 +- .../Sources/ChatControllerAdminBanUsers.swift | 4 +- .../Sources/ChatControllerNode.swift | 10 +- ...rollerOpenMessageReactionContextMenu.swift | 2 +- .../Sources/ChatHistoryListNode.swift | 51 +- .../ChatInterfaceStateContextMenus.swift | 20 +- .../ChatSearchTitleAccessoryPanelNode.swift | 2 +- .../Sources/ChatTranslationPanelNode.swift | 6 +- .../OverlayAudioPlayerControllerNode.swift | 2 +- .../Sources/SharedAccountContext.swift | 4 +- .../WebUI/Sources/WebAppController.swift | 10 +- 103 files changed, 3113 insertions(+), 841 deletions(-) create mode 100644 submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift create mode 100644 submodules/ChatSendMessageActionUI/Sources/MessageItemView.swift create mode 100644 submodules/TelegramCore/Sources/State/AvailableMessageEffects.swift create mode 100644 submodules/TelegramCore/Sources/SyncCore/SyncCore_EffectMessageAttribute.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 7b045f2c18..20e4a3a547 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1097,6 +1097,7 @@ public protocol AccountContext: AnyObject { var animatedEmojiStickersValue: [String: [StickerPackItem]] { get } var additionalAnimatedEmojiStickers: Signal<[String: [Int: StickerPackItem]], NoError> { get } var availableReactions: Signal { get } + var availableMessageEffects: Signal { get } var isPremium: Bool { get } var userLimits: EngineConfiguration.UserLimits { get } diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 4812319634..780cd01564 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -44,6 +44,7 @@ public final class ChatMessageItemAssociatedData: Equatable { public let currentlyPlayingMessageId: EngineMessage.Index? public let isCopyProtectionEnabled: Bool public let availableReactions: AvailableReactions? + public let availableMessageEffects: AvailableMessageEffects? public let savedMessageTags: SavedMessageTags? public let defaultReaction: MessageReaction.Reaction? public let isPremium: Bool @@ -75,6 +76,7 @@ public final class ChatMessageItemAssociatedData: Equatable { currentlyPlayingMessageId: EngineMessage.Index? = nil, isCopyProtectionEnabled: Bool = false, availableReactions: AvailableReactions?, + availableMessageEffects: AvailableMessageEffects?, savedMessageTags: SavedMessageTags?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, @@ -105,6 +107,7 @@ public final class ChatMessageItemAssociatedData: Equatable { self.currentlyPlayingMessageId = currentlyPlayingMessageId self.isCopyProtectionEnabled = isCopyProtectionEnabled self.availableReactions = availableReactions + self.availableMessageEffects = availableMessageEffects self.savedMessageTags = savedMessageTags self.defaultReaction = defaultReaction self.isPremium = isPremium diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 092ea90dba..f848e259ec 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -943,8 +943,8 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { sendWhenOnlineAvailable = false } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputNode.textView, attachment: true, canSendWhenOnline: sendWhenOnlineAvailable, completion: { - }, sendMessage: { [weak textInputPanelNode] mode in + let controller = makeChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputNode.textView, emojiViewProvider: textInputPanelNode.emojiViewProvider, attachment: true, canSendWhenOnline: sendWhenOnlineAvailable, completion: { + }, sendMessage: { [weak textInputPanelNode] mode, _ in switch mode { case .generic: textInputPanelNode?.sendMessage(.generic) @@ -953,10 +953,9 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { case .whenOnline: textInputPanelNode?.sendMessage(.whenOnline) } - }, schedule: { [weak textInputPanelNode] in + }, schedule: { [weak textInputPanelNode] _ in textInputPanelNode?.sendMessage(.schedule) }) - controller.emojiViewProvider = textInputPanelNode.emojiViewProvider strongSelf.presentInGlobalOverlay(controller) }) }, openScheduledMessages: { diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 98f1cec192..160441d7e0 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -493,7 +493,7 @@ public final class CallListController: TelegramBaseController { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_StartNewCall, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let strongSelf = self else { return } diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 4573379393..6cc0ed6c59 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -209,7 +209,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch return filters } |> deliverOnMainQueue).startStandalone(completed: { - c.dismiss(completion: { + c?.dismiss(completion: { chatListController?.present(UndoOverlayController(presentationData: presentationData, content: .chatRemovedFromFolder(chatTitle: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), folderTitle: title), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) @@ -275,7 +275,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch } return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let isPremium = limitsData.0?.isPremium ?? false let (_, limits, premiumLimits) = limitsData @@ -330,10 +330,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) - c.setItems(.single(ContextController.Items(content: .list(updatedItems))), minHeight: nil, animated: true) + c?.setItems(.single(ContextController.Items(content: .list(updatedItems))), minHeight: nil, animated: true) }))) } } @@ -624,7 +624,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: /*subItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator)*/ @@ -657,8 +657,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: } }))) - //c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - c.setItems(.single(ContextController.Items(content: .list(subItems))), minHeight: nil, animated: true) + c?.setItems(.single(ContextController.Items(content: .list(subItems))), minHeight: nil, animated: true) }))) items.append(.separator) @@ -813,7 +812,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: ], title: nil, text: presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) }))) - c.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) + c?.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) } }))) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index cbcc97d831..03fd884181 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1592,7 +1592,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1635,7 +1635,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_AddChatsToFolder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1728,7 +1728,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadAll"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1743,7 +1743,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: filterPeersAreMuted.areMuted ? strongSelf.presentationData.strings.ChatList_ContextUnmuteAll : strongSelf.presentationData.strings.ChatList_ContextMuteAll, textColor: .primary, badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: filterPeersAreMuted.areMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { }) guard let strongSelf = self else { @@ -1786,7 +1786,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ContextMenuShare, textColor: .primary, badge: data.hasSharedLinks ? nil : ContextMenuActionBadge(value: strongSelf.presentationData.strings.ChatList_ContextMenuBadgeNew, color: .accent, style: .label), icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1807,7 +1807,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1819,7 +1819,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolders, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -1833,7 +1833,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReorderTabs, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -2893,7 +2893,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryFeed_ContextAddStory, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -2905,7 +2905,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryFeed_ContextSavedStories, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Stories"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -2917,7 +2917,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryFeed_ContextArchivedStories, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -2939,7 +2939,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: openTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: openIcon), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -3008,7 +3008,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryFeed_ContextOpenChat, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self, let navigationController = self.navigationController as? NavigationController else { return } @@ -3020,7 +3020,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryFeed_ContextOpenProfile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -5705,7 +5705,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: presetList.isEmpty ? strongSelf.presentationData.strings.ChatList_AddFolder : strongSelf.presentationData.strings.ChatList_EditFolders, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: presetList.isEmpty ? "Chat/Context Menu/Add" : "Chat/Context Menu/ItemList"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index be1db5ae75..9a7352e41c 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1026,7 +1026,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in self?.openMessage(EnginePeer(message.peers[message.id.peerId]!), nil, message.id, false) }) }))) @@ -1036,7 +1036,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo items.append(.separator) } items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.dismissInput() @@ -1087,7 +1087,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if let linkForCopying = linkForCopying { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuCopyLink, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: {}) + c?.dismiss(completion: {}) UIPasteboard.general.string = linkForCopying let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -1097,7 +1097,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if !message._asMessage().isCopyProtected() { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in if let strongSelf = self { strongSelf.forwardMessages(messageIds: Set([message.id])) } @@ -1105,14 +1105,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }))) } items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in self?.openMessage(EnginePeer(message.peers[message.id.peerId]!), message.threadId, message.id, false) }) }))) items.append(.separator) items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.dismissInput() @@ -1151,7 +1151,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { self?.openMessage(EnginePeer(message.peers[message.id.peerId]!), message.threadId, message.id, false) }) }))) @@ -1160,7 +1160,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } else { items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.forwardMessages(messageIds: [message.id]) } diff --git a/submodules/ChatSendMessageActionUI/BUILD b/submodules/ChatSendMessageActionUI/BUILD index aaf8818620..e3f895d56d 100644 --- a/submodules/ChatSendMessageActionUI/BUILD +++ b/submodules/ChatSendMessageActionUI/BUILD @@ -14,6 +14,7 @@ swift_library( "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox", "//submodules/AccountContext:AccountContext", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/ContextUI:ContextUI", @@ -24,6 +25,13 @@ swift_library( "//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/ReactionSelectionNode", "//submodules/Components/ReactionButtonListComponent", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/ComponentFlow", + "//submodules/ChatMessageBackground", + "//submodules/WallpaperBackgroundNode", + "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/Components/MultilineTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift index bac82431cd..4fe1ad49d9 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift @@ -9,13 +9,28 @@ import ContextUI import TelegramCore import TextFormat import ReactionSelectionNode +import WallpaperBackgroundNode -public final class ChatSendMessageActionSheetController: ViewController { - public enum SendMode { - case generic - case silently - case whenOnline +public enum ChatSendMessageActionSheetControllerSendMode { + case generic + case silently + case whenOnline +} + +public final class ChatSendMessageActionSheetControllerMessageEffect { + public let id: Int64 + + public init(id: Int64) { + self.id = id } +} + +public protocol ChatSendMessageActionSheetController: ViewController { + typealias SendMode = ChatSendMessageActionSheetControllerSendMode + typealias MessageEffect = ChatSendMessageActionSheetControllerMessageEffect +} + +private final class ChatSendMessageActionSheetControllerImpl: ViewController, ChatSendMessageActionSheetController { private var controllerNode: ChatSendMessageActionSheetControllerNode { return self.displayNode as! ChatSendMessageActionSheetControllerNode } @@ -33,8 +48,8 @@ public final class ChatSendMessageActionSheetController: ViewController { private let attachment: Bool private let canSendWhenOnline: Bool private let completion: () -> Void - private let sendMessage: (SendMode) -> Void - private let schedule: () -> Void + private let sendMessage: (SendMode, MessageEffect?) -> Void + private let schedule: (MessageEffect?) -> Void private let reactionItems: [ReactionItem]? private var presentationData: PresentationData @@ -46,9 +61,9 @@ public final class ChatSendMessageActionSheetController: ViewController { private let hapticFeedback = HapticFeedback() - public var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? + private let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, isScheduledMessages: Bool = false, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputView: UITextView, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode) -> Void, schedule: @escaping () -> Void, reactionItems: [ReactionItem]? = nil) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, isScheduledMessages: Bool = false, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputView: UITextView, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode, MessageEffect?) -> Void, schedule: @escaping (MessageEffect?) -> Void, reactionItems: [ReactionItem]? = nil) { self.context = context self.peerId = peerId self.isScheduledMessages = isScheduledMessages @@ -57,6 +72,7 @@ public final class ChatSendMessageActionSheetController: ViewController { self.gesture = gesture self.sourceSendButton = sourceSendButton self.textInputView = textInputView + self.emojiViewProvider = emojiViewProvider self.attachment = attachment self.canSendWhenOnline = canSendWhenOnline self.completion = completion @@ -111,16 +127,32 @@ public final class ChatSendMessageActionSheetController: ViewController { } self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputView: self.textInputView, attachment: self.attachment, canSendWhenOnline: self.canSendWhenOnline, forwardedCount: forwardedCount, hasEntityKeyboard: self.hasEntityKeyboard, emojiViewProvider: self.emojiViewProvider, send: { [weak self] in - self?.sendMessage(.generic) + var messageEffect: MessageEffect? + if let selectedEffect = self?.controllerNode.selectedMessageEffect { + messageEffect = MessageEffect(id: selectedEffect.id) + } + self?.sendMessage(.generic, messageEffect) self?.dismiss(cancel: false) }, sendSilently: { [weak self] in - self?.sendMessage(.silently) + var messageEffect: MessageEffect? + if let selectedEffect = self?.controllerNode.selectedMessageEffect { + messageEffect = MessageEffect(id: selectedEffect.id) + } + self?.sendMessage(.silently, messageEffect) self?.dismiss(cancel: false) }, sendWhenOnline: { [weak self] in - self?.sendMessage(.whenOnline) + var messageEffect: MessageEffect? + if let selectedEffect = self?.controllerNode.selectedMessageEffect { + messageEffect = MessageEffect(id: selectedEffect.id) + } + self?.sendMessage(.whenOnline, messageEffect) self?.dismiss(cancel: false) }, schedule: !canSchedule ? nil : { [weak self] in - self?.schedule() + var messageEffect: MessageEffect? + if let selectedEffect = self?.controllerNode.selectedMessageEffect { + messageEffect = MessageEffect(id: selectedEffect.id) + } + self?.schedule(messageEffect) self?.dismiss(cancel: false) }, cancel: { [weak self] in self?.dismiss(cancel: true) @@ -160,3 +192,64 @@ public final class ChatSendMessageActionSheetController: ViewController { }) } } + +public func makeChatSendMessageActionSheetController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peerId: EnginePeer.Id?, + isScheduledMessages: Bool = false, + forwardMessageIds: [EngineMessage.Id]?, + hasEntityKeyboard: Bool, + gesture: ContextGesture, + sourceSendButton: ASDisplayNode, + textInputView: UITextView, + emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, + wallpaperBackgroundNode: WallpaperBackgroundNode? = nil, + attachment: Bool = false, + canSendWhenOnline: Bool, + completion: @escaping () -> Void, + sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void, + schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void, + reactionItems: [ReactionItem]? = nil +) -> ChatSendMessageActionSheetController { + if textInputView.text.isEmpty { + return ChatSendMessageActionSheetControllerImpl( + context: context, + updatedPresentationData: updatedPresentationData, + peerId: peerId, + isScheduledMessages: isScheduledMessages, + forwardMessageIds: forwardMessageIds, + hasEntityKeyboard: hasEntityKeyboard, + gesture: gesture, + sourceSendButton: sourceSendButton, + textInputView: textInputView, + emojiViewProvider: emojiViewProvider, + attachment: attachment, + canSendWhenOnline: canSendWhenOnline, + completion: completion, + sendMessage: sendMessage, + schedule: schedule, + reactionItems: reactionItems + ) + } + + return ChatSendMessageContextScreen( + context: context, + updatedPresentationData: updatedPresentationData, + peerId: peerId, + isScheduledMessages: isScheduledMessages, + forwardMessageIds: forwardMessageIds, + hasEntityKeyboard: hasEntityKeyboard, + gesture: gesture, + sourceSendButton: sourceSendButton, + textInputView: textInputView, + emojiViewProvider: emojiViewProvider, + wallpaperBackgroundNode: wallpaperBackgroundNode, + attachment: attachment, + canSendWhenOnline: canSendWhenOnline, + completion: completion, + sendMessage: sendMessage, + schedule: schedule, + reactionItems: reactionItems + ) +} diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift index 4aa9d47e22..54d26b69a0 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift @@ -4,6 +4,7 @@ import AsyncDisplayKit import SwiftSignalKit import Display import TelegramCore +import Postbox import TelegramPresentationData import AccountContext import AppBundle @@ -186,9 +187,10 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, private let toMessageTextScrollView: UIScrollView private let toMessageTextNode: ChatInputTextNode private var messageEffectReactionIcon: ReactionIconView? + private var messageEffectReactionText: ImmediateTextView? private let scrollNode: ASScrollNode - private var selectedMessageEffect: (reaction: MessageReaction.Reaction, file: TelegramMediaFile)? + private(set) var selectedMessageEffect: (id: Int64, effect: AvailableMessageEffects.MessageEffect)? private var reactionContextNode: ReactionContextNode? private var fromCustomEmojiContainerView: CustomEmojiContainerView? @@ -380,7 +382,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - if let reactionItems, !reactionItems.isEmpty { //TODO:localize let reactionContextNode = ReactionContextNode( @@ -394,24 +395,12 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, alwaysAllowPremiumReactions: false, allPresetReactionsAreAvailable: true, getEmojiContent: { animationCache, animationRenderer in - let mappedReactionItems: [EmojiComponentReactionItem] = reactionItems.map { reaction -> EmojiComponentReactionItem in - return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation) - } - - return EmojiPagerContentComponent.emojiInputData( + return EmojiPagerContentComponent.messageEffectsInputData( context: context, animationCache: animationCache, animationRenderer: animationRenderer, - isStandalone: false, - subject: .reaction(onlyTop: false), - hasTrending: false, - topReactionItems: mappedReactionItems, - areUnicodeEmojiEnabled: false, - areCustomEmojiEnabled: true, - chatPeerId: context.account.peerId, - selectedItems: Set(), - hasSearch: false, - premiumIfSavedMessages: false + hasSearch: true, + hideBackground: false ) }, isExpandedUpdated: { [weak self] transition in @@ -438,73 +427,51 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, return } - let reactionItem: Signal - switch updateReaction.reaction { - case .builtin: - reactionItem = context.engine.stickers.availableReactions() - |> take(1) - |> map { availableReactions -> ReactionItem? in - guard let availableReactions else { - return nil - } - for reaction in availableReactions.reactions { - guard let centerAnimation = reaction.centerAnimation else { - continue - } - guard let aroundAnimation = reaction.aroundAnimation else { - continue - } - if reaction.value == updateReaction.reaction { - return ReactionItem( - reaction: ReactionItem.Reaction(rawValue: reaction.value), - appearAnimation: reaction.appearAnimation, - stillAnimation: reaction.selectAnimation, - listAnimation: centerAnimation, - largeListAnimation: reaction.activateAnimation, - applicationAnimation: aroundAnimation, - largeApplicationAnimation: reaction.effectAnimation, - isCustom: false - ) - } - } + guard case let .custom(sourceEffectId, _) = updateReaction else { + return + } + + let messageEffect: Signal + messageEffect = context.engine.stickers.availableMessageEffects() + |> take(1) + |> map { availableMessageEffects -> AvailableMessageEffects.MessageEffect? in + guard let availableMessageEffects else { return nil } - case .custom: - switch updateReaction { - case let .custom(_, file): - if let itemFile = file { - reactionItem = .single(ReactionItem( - reaction: ReactionItem.Reaction(rawValue: updateReaction.reaction), - appearAnimation: itemFile, - stillAnimation: itemFile, - listAnimation: itemFile, - largeListAnimation: itemFile, - applicationAnimation: nil, - largeApplicationAnimation: nil, - isCustom: true - )) - } else { - reactionItem = .single(nil) + for messageEffect in availableMessageEffects.messageEffects { + if messageEffect.id == sourceEffectId || messageEffect.effectSticker.fileId.id == sourceEffectId { + return messageEffect } - default: - reactionItem = .single(nil) } + return nil } self.messageEffectDisposable.set((combineLatest( - reactionItem, + messageEffect, ReactionContextNode.randomGenericReactionEffect(context: context) ) - |> deliverOnMainQueue).startStrict(next: { [weak self] reactionItem, path in + |> deliverOnMainQueue).startStrict(next: { [weak self] messageEffect, path in guard let self else { return } - guard let reactionItem else { + guard let messageEffect else { return } + let effectId = messageEffect.id + + let reactionItem = ReactionItem( + reaction: ReactionItem.Reaction(rawValue: updateReaction.reaction), + appearAnimation: messageEffect.effectSticker, + stillAnimation: messageEffect.effectSticker, + listAnimation: messageEffect.effectSticker, + largeListAnimation: messageEffect.effectSticker, + applicationAnimation: nil, + largeApplicationAnimation: nil, + isCustom: true + ) if let selectedMessageEffect = self.selectedMessageEffect { - if selectedMessageEffect.reaction == updateReaction.reaction { + if selectedMessageEffect.id == effectId { self.selectedMessageEffect = nil reactionContextNode.selectedItems = Set([]) @@ -518,17 +485,17 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.update(transition: .animated(duration: 0.2, curve: .easeInOut)) return } else { - self.selectedMessageEffect = (reaction: updateReaction.reaction, file: reactionItem.listAnimation) + self.selectedMessageEffect = (id: effectId, effect: messageEffect) reactionContextNode.selectedItems = Set([AnyHashable(updateReaction.reaction)]) self.update(transition: .animated(duration: 0.2, curve: .easeInOut)) } } else { - self.selectedMessageEffect = (reaction: updateReaction.reaction, file: reactionItem.listAnimation) + self.selectedMessageEffect = (id: effectId, effect: messageEffect) reactionContextNode.selectedItems = Set([AnyHashable(updateReaction.reaction)]) self.update(transition: .animated(duration: 0.2, curve: .easeInOut)) } - guard let messageEffectReactionIcon = self.messageEffectReactionIcon else { + guard let targetView = self.messageEffectReactionIcon ?? self.messageEffectReactionText else { return } @@ -546,16 +513,27 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.standaloneReactionAnimation = standaloneReactionAnimation self.addSubnode(standaloneReactionAnimation) + var customEffectResource: MediaResource? + if let effectAnimation = messageEffect.effectAnimation { + customEffectResource = effectAnimation.resource + } else { + let effectSticker = messageEffect.effectSticker + if let effectFile = effectSticker.videoThumbnails.first { + customEffectResource = effectFile.resource + } + } + standaloneReactionAnimation.animateReactionSelection( context: context, theme: self.presentationData.theme, animationCache: context.animationCache, reaction: reactionItem, + customEffectResource: customEffectResource, avatarPeers: [], playHaptic: true, isLarge: true, playCenterReaction: false, - targetView: messageEffectReactionIcon, + targetView: targetView, addStandaloneReactionAnimation: { standaloneReactionAnimation in /*guard let strongSelf = self else { return @@ -899,6 +877,9 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } } else { completedBubble = true + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.animateOut(to: nil, animatingOutToReaction: false) + } } let contentOffset = CGPoint(x: self.sendButtonFrame.midX - self.contentContainerNode.frame.midX, y: self.sendButtonFrame.midY - self.contentContainerNode.frame.midY) @@ -1048,35 +1029,89 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.toMessageTextNode.updateLayout(size: textFrame.size) if let selectedMessageEffect = self.selectedMessageEffect { - let messageEffectReactionIcon: ReactionIconView - var iconTransition = transition - var animateIn = false - if let current = self.messageEffectReactionIcon { - messageEffectReactionIcon = current + if let iconFile = selectedMessageEffect.effect.staticIcon { + let messageEffectReactionIcon: ReactionIconView + var iconTransition = transition + var animateIn = false + if let current = self.messageEffectReactionIcon { + messageEffectReactionIcon = current + } else { + iconTransition = .immediate + animateIn = true + messageEffectReactionIcon = ReactionIconView(frame: CGRect()) + self.messageEffectReactionIcon = messageEffectReactionIcon + self.toMessageTextScrollView.addSubview(messageEffectReactionIcon) + } + + let iconSize = CGSize(width: 10.0, height: 10.0) + messageEffectReactionIcon.update(size: iconSize, context: self.context, file: iconFile, fileId: iconFile.fileId.id, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, tintColor: nil, placeholderColor: self.presentationData.theme.chat.message.stickerPlaceholderColor.withWallpaper, animateIdle: false, reaction: .custom(selectedMessageEffect.id), transition: iconTransition) + + let iconFrame = CGRect(origin: CGPoint(x: self.toMessageTextNode.frame.minX + messageFrame.width - 30.0 + 2.0 - iconSize.width, y: self.toMessageTextNode.frame.maxY - 6.0 - iconSize.height), size: iconSize) + iconTransition.updateFrame(view: messageEffectReactionIcon, frame: iconFrame) + if animateIn && transition.isAnimated { + transition.animateTransformScale(view: messageEffectReactionIcon, from: 0.001) + messageEffectReactionIcon.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + + if let messageEffectReactionText = self.messageEffectReactionText { + self.messageEffectReactionText = nil + + transition.updateTransformScale(layer: messageEffectReactionText.layer, scale: 0.001) + transition.updateAlpha(layer: messageEffectReactionText.layer, alpha: 0.0, completion: { [weak messageEffectReactionText] _ in + messageEffectReactionText?.removeFromSuperview() + }) + } } else { - iconTransition = .immediate - animateIn = true - messageEffectReactionIcon = ReactionIconView(frame: CGRect()) - self.messageEffectReactionIcon = messageEffectReactionIcon - self.toMessageTextScrollView.addSubview(messageEffectReactionIcon) + let messageEffectReactionText: ImmediateTextView + var iconTransition = transition + var animateIn = false + if let current = self.messageEffectReactionText { + messageEffectReactionText = current + } else { + iconTransition = .immediate + animateIn = true + messageEffectReactionText = ImmediateTextView() + self.messageEffectReactionText = messageEffectReactionText + self.toMessageTextScrollView.addSubview(messageEffectReactionText) + } + + let iconSize = CGSize(width: 10.0, height: 10.0) + messageEffectReactionText.attributedText = NSAttributedString(string: selectedMessageEffect.effect.emoticon, font: Font.regular(9.0), textColor: .black) + let textSize = messageEffectReactionText.updateLayout(CGSize(width: 100.0, height: 100.0)) + + let iconFrame = CGRect(origin: CGPoint(x: self.toMessageTextNode.frame.minX + messageFrame.width - 30.0 + 2.0 - iconSize.width + floorToScreenPixels((iconSize.width - textSize.width) * 0.5), y: self.toMessageTextNode.frame.maxY - 6.0 - iconSize.height + floorToScreenPixels((iconSize.height - textSize.height) * 0.5)), size: textSize) + iconTransition.updateFrame(view: messageEffectReactionText, frame: iconFrame) + if animateIn && transition.isAnimated { + transition.animateTransformScale(view: messageEffectReactionText, from: 0.001) + messageEffectReactionText.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + + if let messageEffectReactionIcon = self.messageEffectReactionIcon { + self.messageEffectReactionIcon = nil + + transition.updateTransformScale(layer: messageEffectReactionIcon.layer, scale: 0.001) + transition.updateAlpha(layer: messageEffectReactionIcon.layer, alpha: 0.0, completion: { [weak messageEffectReactionIcon] _ in + messageEffectReactionIcon?.removeFromSuperview() + }) + } } - - let iconSize = CGSize(width: 10.0, height: 10.0) - messageEffectReactionIcon.update(size: iconSize, context: self.context, file: selectedMessageEffect.file, fileId: selectedMessageEffect.file.fileId.id, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, tintColor: nil, placeholderColor: self.presentationData.theme.chat.message.stickerPlaceholderColor.withWallpaper, animateIdle: false, reaction: selectedMessageEffect.reaction, transition: iconTransition) - - let iconFrame = CGRect(origin: CGPoint(x: self.toMessageTextNode.frame.minX + messageFrame.width - 30.0 + 2.0 - iconSize.width, y: self.toMessageTextNode.frame.maxY - 6.0 - iconSize.height), size: iconSize) - iconTransition.updateFrame(view: messageEffectReactionIcon, frame: iconFrame) - if animateIn && transition.isAnimated { - transition.animateTransformScale(view: messageEffectReactionIcon, from: 0.001) - messageEffectReactionIcon.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } else { + if let messageEffectReactionIcon = self.messageEffectReactionIcon { + self.messageEffectReactionIcon = nil + + transition.updateTransformScale(layer: messageEffectReactionIcon.layer, scale: 0.001) + transition.updateAlpha(layer: messageEffectReactionIcon.layer, alpha: 0.0, completion: { [weak messageEffectReactionIcon] _ in + messageEffectReactionIcon?.removeFromSuperview() + }) + } + if let messageEffectReactionText = self.messageEffectReactionText { + self.messageEffectReactionText = nil + + transition.updateTransformScale(layer: messageEffectReactionText.layer, scale: 0.001) + transition.updateAlpha(layer: messageEffectReactionText.layer, alpha: 0.0, completion: { [weak messageEffectReactionText] _ in + messageEffectReactionText?.removeFromSuperview() + }) } - } else if let messageEffectReactionIcon = self.messageEffectReactionIcon { - self.messageEffectReactionIcon = nil - - transition.updateTransformScale(layer: messageEffectReactionIcon.layer, scale: 0.001) - transition.updateAlpha(layer: messageEffectReactionIcon.layer, alpha: 0.0, completion: { [weak messageEffectReactionIcon] _ in - messageEffectReactionIcon?.removeFromSuperview() - }) } if let reactionContextNode = self.reactionContextNode { diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift new file mode 100644 index 0000000000..f426a02add --- /dev/null +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift @@ -0,0 +1,860 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramPresentationData +import AccountContext +import ContextUI +import TelegramCore +import Postbox +import TextFormat +import ReactionSelectionNode +import ViewControllerComponent +import ComponentFlow +import ComponentDisplayAdapters +import WallpaperBackgroundNode +import ReactionSelectionNode +import EntityKeyboard + +func convertFrame(_ frame: CGRect, from fromView: UIView, to toView: UIView) -> CGRect { + let sourceWindowFrame = fromView.convert(frame, to: nil) + var targetWindowFrame = toView.convert(sourceWindowFrame, from: nil) + + if let fromWindow = fromView.window, let toWindow = toView.window { + targetWindowFrame.origin.x += toWindow.bounds.width - fromWindow.bounds.width + } + return targetWindowFrame +} + +final class ChatSendMessageContextScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let peerId: EnginePeer.Id? + let isScheduledMessages: Bool + let forwardMessageIds: [EngineMessage.Id]? + let hasEntityKeyboard: Bool + let gesture: ContextGesture + let sourceSendButton: ASDisplayNode + let textInputView: UITextView + let emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? + let wallpaperBackgroundNode: WallpaperBackgroundNode? + let attachment: Bool + let canSendWhenOnline: Bool + let completion: () -> Void + let sendMessage: (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void + let schedule: (ChatSendMessageActionSheetController.MessageEffect?) -> Void + let reactionItems: [ReactionItem]? + + init( + context: AccountContext, + peerId: EnginePeer.Id?, + isScheduledMessages: Bool, + forwardMessageIds: [EngineMessage.Id]?, + hasEntityKeyboard: Bool, + gesture: ContextGesture, + sourceSendButton: ASDisplayNode, + textInputView: UITextView, + emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, + wallpaperBackgroundNode: WallpaperBackgroundNode?, + attachment: Bool, + canSendWhenOnline: Bool, + completion: @escaping () -> Void, + sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void, + schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void, + reactionItems: [ReactionItem]? + ) { + self.context = context + self.peerId = peerId + self.isScheduledMessages = isScheduledMessages + self.forwardMessageIds = forwardMessageIds + self.hasEntityKeyboard = hasEntityKeyboard + self.gesture = gesture + self.sourceSendButton = sourceSendButton + self.textInputView = textInputView + self.emojiViewProvider = emojiViewProvider + self.wallpaperBackgroundNode = wallpaperBackgroundNode + self.attachment = attachment + self.canSendWhenOnline = canSendWhenOnline + self.completion = completion + self.sendMessage = sendMessage + self.schedule = schedule + self.reactionItems = reactionItems + } + + static func ==(lhs: ChatSendMessageContextScreenComponent, rhs: ChatSendMessageContextScreenComponent) -> Bool { + return true + } + + enum PresentationAnimationState { + enum Key { + case initial + case animatedIn + case animatedOut + } + + case initial + case animatedIn + case animatedOut(completion: () -> Void) + + var key: Key { + switch self { + case .initial: + return .initial + case .animatedIn: + return .animatedIn + case .animatedOut: + return .animatedOut + } + } + } + + final class View: UIView { + private let backgroundView: BlurredBackgroundView + + private var sendButton: HighlightTrackingButton? + private var messageItemView: MessageItemView? + private var actionsStackNode: ContextControllerActionsStackNode? + private var reactionContextNode: ReactionContextNode? + + private let scrollView: UIScrollView + + private var component: ChatSendMessageContextScreenComponent? + private var environment: EnvironmentType? + private weak var state: EmptyComponentState? + private var isUpdating: Bool = false + + private let messageEffectDisposable = MetaDisposable() + private var selectedMessageEffect: AvailableMessageEffects.MessageEffect? + private var standaloneReactionAnimation: StandaloneReactionAnimation? + + private var presentationAnimationState: PresentationAnimationState = .initial + private var appliedAnimationState: PresentationAnimationState = .initial + private var animateOutToEmpty: Bool = false + + override init(frame: CGRect) { + self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) + + self.scrollView = UIScrollView() + self.scrollView.showsVerticalScrollIndicator = true + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.scrollsToTop = false + self.scrollView.delaysContentTouches = false + self.scrollView.canCancelContentTouches = true + self.scrollView.contentInsetAdjustmentBehavior = .never + self.scrollView.alwaysBounceVertical = true + + super.init(frame: frame) + + self.addSubview(self.backgroundView) + + self.addSubview(self.scrollView) + + self.backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onBackgroundTap(_:)))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.messageEffectDisposable.dispose() + } + + @objc private func onBackgroundTap(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.environment?.controller()?.dismiss() + } + } + + @objc private func onSendButtonPressed() { + guard let component = self.component else { + return + } + self.animateOutToEmpty = true + component.sendMessage(.generic, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) })) + self.environment?.controller()?.dismiss() + } + + func animateIn() { + if case .initial = self.presentationAnimationState { + self.presentationAnimationState = .animatedIn + self.state?.updated(transition: .spring(duration: 0.42)) + } + } + + func animateOut(completion: @escaping () -> Void) { + if case .animatedOut = self.presentationAnimationState { + } else { + self.presentationAnimationState = .animatedOut(completion: completion) + self.state?.updated(transition: .spring(duration: 0.4)) + } + } + + func update(component: ChatSendMessageContextScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + let previousAnimationState = self.appliedAnimationState + self.appliedAnimationState = self.presentationAnimationState + + let messageActionsSpacing: CGFloat = 7.0 + + let alphaTransition: Transition + if transition.animation.isImmediate { + alphaTransition = .immediate + } else { + alphaTransition = .easeInOut(duration: 0.25) + } + let _ = alphaTransition + + let environment = environment[EnvironmentType.self].value + + let themeUpdated = environment.theme !== self.environment?.theme + + if self.component == nil { + component.gesture.externalUpdated = { [weak self] view, location in + guard let self, let actionsStackNode = self.actionsStackNode else { + return + } + actionsStackNode.highlightGestureMoved(location: actionsStackNode.view.convert(location, from: view)) + } + component.gesture.externalEnded = { [weak self] viewAndLocation in + guard let self, let actionsStackNode = self.actionsStackNode else { + return + } + if let (view, location) = viewAndLocation { + actionsStackNode.highlightGestureMoved(location: actionsStackNode.view.convert(location, from: view)) + actionsStackNode.highlightGestureFinished(performAction: true) + } else { + actionsStackNode.highlightGestureFinished(performAction: false) + } + } + } + + self.component = component + self.environment = environment + self.state = state + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) + + if themeUpdated { + self.backgroundView.updateColor( + color: environment.theme.contextMenu.dimColor, + enableBlur: true, + forceKeepBlur: true, + transition: .immediate + ) + } + + let sendButton: HighlightTrackingButton + if let current = self.sendButton { + sendButton = current + } else { + sendButton = HighlightTrackingButton() + sendButton.accessibilityLabel = environment.strings.MediaPicker_Send + sendButton.addTarget(self, action: #selector(self.onSendButtonPressed), for: .touchUpInside) + if let snapshotView = component.sourceSendButton.view.snapshotView(afterScreenUpdates: false) { + snapshotView.isUserInteractionEnabled = false + sendButton.addSubview(snapshotView) + } + self.sendButton = sendButton + self.addSubview(sendButton) + } + + let sourceSendButtonFrame = convertFrame(component.sourceSendButton.bounds, from: component.sourceSendButton.view, to: self) + + let sendButtonScale: CGFloat + switch self.presentationAnimationState { + case .initial: + sendButtonScale = 0.75 + default: + sendButtonScale = 1.0 + } + + let messageItemView: MessageItemView + if let current = self.messageItemView { + messageItemView = current + } else { + messageItemView = MessageItemView(frame: CGRect()) + self.messageItemView = messageItemView + self.addSubview(messageItemView) + } + + let textString: NSAttributedString + if let attributedText = component.textInputView.attributedText { + textString = attributedText + } else { + textString = NSAttributedString(string: " ", font: Font.regular(17.0), textColor: .black) + } + + let localSourceTextInputViewFrame = convertFrame(component.textInputView.bounds, from: component.textInputView, to: self) + + let sourceMessageTextInsets = UIEdgeInsets(top: 7.0, left: 12.0, bottom: 6.0, right: 20.0) + let sourceBackgroundSize = CGSize(width: localSourceTextInputViewFrame.width + 32.0, height: localSourceTextInputViewFrame.height + 4.0) + let explicitMessageBackgroundSize: CGSize? + switch self.presentationAnimationState { + case .initial: + explicitMessageBackgroundSize = sourceBackgroundSize + case .animatedOut: + if self.animateOutToEmpty { + explicitMessageBackgroundSize = nil + } else { + explicitMessageBackgroundSize = sourceBackgroundSize + } + case .animatedIn: + explicitMessageBackgroundSize = nil + } + + let messageTextInsets = sourceMessageTextInsets + + let messageItemSize = messageItemView.update( + context: component.context, + presentationData: presentationData, + backgroundNode: component.wallpaperBackgroundNode, + textString: textString, + textInsets: messageTextInsets, + explicitBackgroundSize: explicitMessageBackgroundSize, + maxTextWidth: localSourceTextInputViewFrame.width - 32.0, + effect: self.presentationAnimationState.key == .animatedIn ? self.selectedMessageEffect : nil, + transition: transition + ) + let sourceMessageItemFrame = CGRect(origin: CGPoint(x: localSourceTextInputViewFrame.minX - sourceMessageTextInsets.left, y: localSourceTextInputViewFrame.minY - 2.0), size: messageItemSize) + + let actionsStackNode: ContextControllerActionsStackNode + if let current = self.actionsStackNode { + actionsStackNode = current + } else { + actionsStackNode = ContextControllerActionsStackNode( + getController: { + return nil + }, + requestDismiss: { _ in + }, + requestUpdate: { [weak self] transition in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: Transition(transition)) + } + } + ) + actionsStackNode.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0) + + var reminders = false + var isSecret = false + var canSchedule = false + if let peerId = component.peerId { + reminders = peerId == component.context.account.peerId + isSecret = peerId.namespace == Namespaces.Peer.SecretChat + canSchedule = !isSecret + } + if component.isScheduledMessages { + canSchedule = false + } + + var items: [ContextMenuItem] = [] + if !reminders { + items.append(.action(ContextMenuActionItem( + text: environment.strings.Conversation_SendMessage_SendSilently, + icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, _ in + guard let self, let component = self.component else { + return + } + self.animateOutToEmpty = true + component.sendMessage(.silently, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) })) + self.environment?.controller()?.dismiss() + } + ))) + + if component.canSendWhenOnline && canSchedule { + items.append(.action(ContextMenuActionItem( + text: environment.strings.Conversation_SendMessage_SendWhenOnline, + icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/WhenOnlineIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, _ in + guard let self, let component = self.component else { + return + } + self.animateOutToEmpty = true + component.sendMessage(.whenOnline, self.selectedMessageEffect.flatMap({ ChatSendMessageActionSheetController.MessageEffect(id: $0.id) })) + self.environment?.controller()?.dismiss() + } + ))) + } + } + if canSchedule { + items.append(.action(ContextMenuActionItem( + text: reminders ? environment.strings.Conversation_SendMessage_SetReminder: environment.strings.Conversation_SendMessage_ScheduleMessage, + icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, _ in + guard let self, let component = self.component else { + return + } + component.schedule(nil) + self.environment?.controller()?.dismiss() + } + ))) + } + + actionsStackNode.push( + item: ContextControllerActionsListStackItem( + id: nil, + items: items, + reactionItems: nil, + tip: nil, + tipSignal: .single(nil), + dismissed: nil + ), + currentScrollingState: nil, + positionLock: nil, + animated: false + ) + self.actionsStackNode = actionsStackNode + self.addSubview(actionsStackNode.view) + } + let actionsStackSize = actionsStackNode.update( + presentationData: presentationData, + constrainedSize: availableSize, + presentation: .modal, + transition: transition.containedViewLayoutTransition + ) + let sourceActionsStackFrame = CGRect(origin: CGPoint(x: sourceSendButtonFrame.minX + 1.0 - actionsStackSize.width, y: sourceMessageItemFrame.maxY + messageActionsSpacing), size: actionsStackSize) + + if let reactionItems = component.reactionItems, !reactionItems.isEmpty { + let reactionContextNode: ReactionContextNode + if let current = self.reactionContextNode { + reactionContextNode = current + } else { + //TODO:localize + reactionContextNode = ReactionContextNode( + context: component.context, + animationCache: component.context.animationCache, + presentationData: presentationData, + items: reactionItems.map(ReactionContextItem.reaction), + selectedItems: Set(), + title: "Add an animated effect", + reactionsLocked: false, + alwaysAllowPremiumReactions: false, + allPresetReactionsAreAvailable: true, + getEmojiContent: { animationCache, animationRenderer in + return EmojiPagerContentComponent.messageEffectsInputData( + context: component.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + hasSearch: true, + hideBackground: false + ) + }, + isExpandedUpdated: { [weak self] transition in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: Transition(transition)) + } + }, + requestLayout: { [weak self] transition in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: Transition(transition)) + } + }, + requestUpdateOverlayWantsToBeBelowKeyboard: { [weak self] transition in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: Transition(transition)) + } + } + ) + reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in + guard let self, let component = self.component, let reactionContextNode = self.reactionContextNode else { + return + } + + guard case let .custom(sourceEffectId, _) = updateReaction else { + return + } + + let messageEffect: Signal + messageEffect = component.context.engine.stickers.availableMessageEffects() + |> take(1) + |> map { availableMessageEffects -> AvailableMessageEffects.MessageEffect? in + guard let availableMessageEffects else { + return nil + } + for messageEffect in availableMessageEffects.messageEffects { + if messageEffect.id == sourceEffectId || messageEffect.effectSticker.fileId.id == sourceEffectId { + return messageEffect + } + } + return nil + } + + self.messageEffectDisposable.set((combineLatest( + messageEffect, + ReactionContextNode.randomGenericReactionEffect(context: component.context) + ) + |> deliverOnMainQueue).startStrict(next: { [weak self] messageEffect, path in + guard let self, let component = self.component, let environment = self.environment else { + return + } + guard let messageEffect else { + return + } + let effectId = messageEffect.id + + let reactionItem = ReactionItem( + reaction: ReactionItem.Reaction(rawValue: updateReaction.reaction), + appearAnimation: messageEffect.effectSticker, + stillAnimation: messageEffect.effectSticker, + listAnimation: messageEffect.effectSticker, + largeListAnimation: messageEffect.effectSticker, + applicationAnimation: nil, + largeApplicationAnimation: nil, + isCustom: true + ) + + if let selectedMessageEffect = self.selectedMessageEffect { + if selectedMessageEffect.id == effectId { + self.selectedMessageEffect = nil + reactionContextNode.selectedItems = Set([]) + + if let standaloneReactionAnimation = self.standaloneReactionAnimation { + self.standaloneReactionAnimation = nil + standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in + standaloneReactionAnimation?.removeFromSupernode() + }) + } + + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + return + } else { + self.selectedMessageEffect = messageEffect + reactionContextNode.selectedItems = Set([AnyHashable(updateReaction.reaction)]) + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + } + } else { + self.selectedMessageEffect = messageEffect + reactionContextNode.selectedItems = Set([AnyHashable(updateReaction.reaction)]) + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + } + + guard let targetView = self.messageItemView?.effectIconView else { + return + } + + if let standaloneReactionAnimation = self.standaloneReactionAnimation { + self.standaloneReactionAnimation = nil + standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in + standaloneReactionAnimation?.removeFromSupernode() + }) + } + + let genericReactionEffect = path + + let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: genericReactionEffect) + standaloneReactionAnimation.frame = self.bounds + self.standaloneReactionAnimation = standaloneReactionAnimation + self.addSubnode(standaloneReactionAnimation) + + var customEffectResource: MediaResource? + if let effectAnimation = messageEffect.effectAnimation { + customEffectResource = effectAnimation.resource + } else { + let effectSticker = messageEffect.effectSticker + if let effectFile = effectSticker.videoThumbnails.first { + customEffectResource = effectFile.resource + } + } + + standaloneReactionAnimation.animateReactionSelection( + context: component.context, + theme: environment.theme, + animationCache: component.context.animationCache, + reaction: reactionItem, + customEffectResource: customEffectResource, + avatarPeers: [], + playHaptic: true, + isLarge: true, + playCenterReaction: false, + targetView: targetView, + addStandaloneReactionAnimation: { _ in + }, + completion: { [weak standaloneReactionAnimation] in + standaloneReactionAnimation?.removeFromSupernode() + } + ) + })) + } + reactionContextNode.displayTail = true + reactionContextNode.forceTailToRight = false + reactionContextNode.forceDark = false + self.reactionContextNode = reactionContextNode + self.addSubview(reactionContextNode.view) + } + } + + var readySendButtonFrame = CGRect(origin: CGPoint(x: sourceSendButtonFrame.minX, y: sourceSendButtonFrame.minY), size: sourceSendButtonFrame.size) + var readyMessageItemFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 8.0 - messageItemSize.width, y: readySendButtonFrame.maxY - 6.0 - messageItemSize.height), size: messageItemSize) + var readyActionsStackFrame = CGRect(origin: CGPoint(x: readySendButtonFrame.minX + 1.0 - actionsStackSize.width, y: readyMessageItemFrame.maxY + messageActionsSpacing), size: actionsStackSize) + + let bottomOverflow = readyActionsStackFrame.maxY - (availableSize.height - environment.safeInsets.bottom) + if bottomOverflow > 0.0 { + readyMessageItemFrame.origin.y -= bottomOverflow + readyActionsStackFrame.origin.y -= bottomOverflow + readySendButtonFrame.origin.y -= bottomOverflow + } + + let messageItemFrame: CGRect + let actionsStackFrame: CGRect + let sendButtonFrame: CGRect + switch self.presentationAnimationState { + case .initial: + messageItemFrame = sourceMessageItemFrame + actionsStackFrame = sourceActionsStackFrame + sendButtonFrame = sourceSendButtonFrame + case .animatedOut: + if self.animateOutToEmpty { + messageItemFrame = readyMessageItemFrame + actionsStackFrame = readyActionsStackFrame + sendButtonFrame = readySendButtonFrame + } else { + messageItemFrame = sourceMessageItemFrame + actionsStackFrame = sourceActionsStackFrame + sendButtonFrame = sourceSendButtonFrame + } + case .animatedIn: + messageItemFrame = readyMessageItemFrame + actionsStackFrame = readyActionsStackFrame + sendButtonFrame = readySendButtonFrame + } + + transition.setFrame(view: messageItemView, frame: messageItemFrame) + + transition.setPosition(view: actionsStackNode.view, position: CGPoint(x: actionsStackFrame.maxX, y: actionsStackFrame.minY)) + transition.setBounds(view: actionsStackNode.view, bounds: CGRect(origin: CGPoint(), size: actionsStackFrame.size)) + if !transition.animation.isImmediate && previousAnimationState.key != self.presentationAnimationState.key { + switch self.presentationAnimationState { + case .initial: + break + case .animatedIn: + transition.setAlpha(view: actionsStackNode.view, alpha: 1.0) + Transition.immediate.setScale(view: actionsStackNode.view, scale: 1.0) + actionsStackNode.layer.animateSpring(from: 0.001 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.42, damping: 104.0) + case .animatedOut: + transition.setAlpha(view: actionsStackNode.view, alpha: 0.0) + transition.setScale(view: actionsStackNode.view, scale: 0.001) + } + } else { + switch self.presentationAnimationState { + case .animatedIn: + transition.setAlpha(view: actionsStackNode.view, alpha: 1.0) + transition.setScale(view: actionsStackNode.view, scale: 1.0) + case .animatedOut, .initial: + transition.setAlpha(view: actionsStackNode.view, alpha: 0.0) + transition.setScale(view: actionsStackNode.view, scale: 0.001) + } + } + + if let reactionContextNode = self.reactionContextNode { + let isFirstTime = reactionContextNode.bounds.isEmpty + + let size = availableSize + let reactionsAnchorRect = messageItemFrame + transition.setFrame(view: reactionContextNode.view, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + reactionContextNode.updateLayout(size: size, insets: UIEdgeInsets(), anchorRect: reactionsAnchorRect, centerAligned: false, isCoveredByInput: false, isAnimatingOut: false, transition: transition.containedViewLayoutTransition) + reactionContextNode.updateIsIntersectingContent(isIntersectingContent: false, transition: .immediate) + if isFirstTime { + reactionContextNode.animateIn(from: reactionsAnchorRect) + } else if self.presentationAnimationState.key == .animatedOut && previousAnimationState.key == .animatedIn { + reactionContextNode.animateOut(to: nil, animatingOutToReaction: false) + } + } + if case .animatedOut = self.presentationAnimationState { + if let standaloneReactionAnimation = self.standaloneReactionAnimation { + self.standaloneReactionAnimation = nil + standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in + standaloneReactionAnimation?.removeFromSupernode() + }) + } + } + + transition.setPosition(view: sendButton, position: sendButtonFrame.center) + transition.setBounds(view: sendButton, bounds: CGRect(origin: CGPoint(), size: sendButtonFrame.size)) + transition.setScale(view: sendButton, scale: sendButtonScale) + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize)) + self.backgroundView.update(size: availableSize, transition: transition.containedViewLayoutTransition) + let backgroundAlpha: CGFloat + switch self.presentationAnimationState { + case .animatedIn: + component.textInputView.isHidden = true + component.sourceSendButton.isHidden = true + + backgroundAlpha = 1.0 + case .animatedOut: + backgroundAlpha = 0.0 + + if self.animateOutToEmpty { + component.textInputView.isHidden = false + component.sourceSendButton.isHidden = false + + transition.setAlpha(view: sendButton, alpha: 0.0) + if let messageItemView = self.messageItemView { + transition.setAlpha(view: messageItemView, alpha: 0.0) + } + } + default: + backgroundAlpha = 0.0 + } + + transition.setAlpha(view: self.backgroundView, alpha: backgroundAlpha, completion: { [weak self] _ in + guard let self else { + return + } + if case let .animatedOut(completion) = self.presentationAnimationState { + if let component = self.component, !self.animateOutToEmpty { + component.textInputView.isHidden = false + component.sourceSendButton.isHidden = false + } + completion() + } + }) + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class ChatSendMessageContextScreen: ViewControllerComponentContainer, ChatSendMessageActionSheetController { + private let context: AccountContext + + private var processedDidAppear: Bool = false + private var processedDidDisappear: Bool = false + + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)?, + peerId: EnginePeer.Id?, + isScheduledMessages: Bool, + forwardMessageIds: [EngineMessage.Id]?, + hasEntityKeyboard: Bool, + gesture: ContextGesture, + sourceSendButton: ASDisplayNode, + textInputView: UITextView, + emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, + wallpaperBackgroundNode: WallpaperBackgroundNode?, + attachment: Bool, + canSendWhenOnline: Bool, + completion: @escaping () -> Void, + sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.MessageEffect?) -> Void, + schedule: @escaping (ChatSendMessageActionSheetController.MessageEffect?) -> Void, + reactionItems: [ReactionItem]? + ) { + self.context = context + + super.init( + context: context, + component: ChatSendMessageContextScreenComponent( + context: context, + peerId: peerId, + isScheduledMessages: isScheduledMessages, + forwardMessageIds: forwardMessageIds, + hasEntityKeyboard: hasEntityKeyboard, + gesture: gesture, + sourceSendButton: sourceSendButton, + textInputView: textInputView, + emojiViewProvider: emojiViewProvider, + wallpaperBackgroundNode: wallpaperBackgroundNode, + attachment: attachment, + canSendWhenOnline: canSendWhenOnline, + completion: completion, + sendMessage: sendMessage, + schedule: schedule, + reactionItems: reactionItems + ), + navigationBarAppearance: .none, + statusBarStyle: .none, + presentationMode: .default + ) + + self.lockOrientation = true + self.blocksBackgroundWhenInOverlay = true + + /*gesture.externalEnded = { [weak self] _ in + guard let self else { + return + } + self.dismiss() + }*/ + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !self.processedDidAppear { + self.processedDidAppear = true + if let componentView = self.node.hostView.componentView as? ChatSendMessageContextScreenComponent.View { + componentView.animateIn() + } + } + } + + private func superDismiss() { + super.dismiss() + } + + override public func dismiss(completion: (() -> Void)? = nil) { + if !self.processedDidDisappear { + self.processedDidDisappear = true + + if let componentView = self.node.hostView.componentView as? ChatSendMessageContextScreenComponent.View { + componentView.animateOut(completion: { [weak self] in + if let self { + self.superDismiss() + } + completion?() + }) + } else { + super.dismiss(completion: completion) + } + } + } +} diff --git a/submodules/ChatSendMessageActionUI/Sources/MessageItemView.swift b/submodules/ChatSendMessageActionUI/Sources/MessageItemView.swift new file mode 100644 index 0000000000..a03dc2f21d --- /dev/null +++ b/submodules/ChatSendMessageActionUI/Sources/MessageItemView.swift @@ -0,0 +1,318 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramPresentationData +import AccountContext +import ContextUI +import TelegramCore +import TextFormat +import ReactionSelectionNode +import ViewControllerComponent +import ComponentFlow +import ComponentDisplayAdapters +import ChatMessageBackground +import WallpaperBackgroundNode +import MultilineTextWithEntitiesComponent +import ReactionButtonListComponent +import MultilineTextComponent + +private final class EffectIcon: Component { + enum Content: Equatable { + case file(TelegramMediaFile) + case text(String) + } + + let context: AccountContext + let content: Content + + init( + context: AccountContext, + content: Content + ) { + self.context = context + self.content = content + } + + static func ==(lhs: EffectIcon, rhs: EffectIcon) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.content != rhs.content { + return false + } + return true + } + + final class View: UIView { + private var fileView: ReactionIconView? + private var textView: ComponentView? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: EffectIcon, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + if case let .file(file) = component.content { + let fileView: ReactionIconView + if let current = self.fileView { + fileView = current + } else { + fileView = ReactionIconView() + self.fileView = fileView + self.addSubview(fileView) + } + fileView.update( + size: availableSize, + context: component.context, + file: file, + fileId: file.fileId.id, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + tintColor: nil, + placeholderColor: UIColor(white: 0.0, alpha: 0.1), + animateIdle: false, + reaction: .custom(file.fileId.id), + transition: .immediate + ) + fileView.frame = CGRect(origin: CGPoint(), size: availableSize) + } else { + if let fileView = self.fileView { + self.fileView = nil + fileView.removeFromSuperview() + } + } + + if case let .text(text) = component.content { + let textView: ComponentView + if let current = self.textView { + textView = current + } else { + textView = ComponentView() + self.textView = textView + } + let textSize = textView.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: text, font: Font.regular(10.0), textColor: .black)) + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - textSize.width) * 0.5), y: floorToScreenPixels((availableSize.height - textSize.height) * 0.5)), size: textSize) + if let textComponentView = textView.view { + if textComponentView.superview == nil { + self.addSubview(textComponentView) + } + textComponentView.frame = textFrame + } + } else { + if let textView = self.textView { + self.textView = nil + textView.view?.removeFromSuperview() + } + } + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +final class MessageItemView: UIView { + private let backgroundWallpaperNode: ChatMessageBubbleBackdrop + private let backgroundNode: ChatMessageBackground + + private let text = ComponentView() + + private var effectIcon: ComponentView? + var effectIconView: UIView? { + return self.effectIcon?.view + } + + private var chatTheme: ChatPresentationThemeData? + private var currentSize: CGSize? + + override init(frame: CGRect) { + self.backgroundWallpaperNode = ChatMessageBubbleBackdrop() + self.backgroundNode = ChatMessageBackground() + self.backgroundNode.backdropNode = self.backgroundWallpaperNode + + super.init(frame: frame) + + self.addSubview(self.backgroundWallpaperNode.view) + self.addSubview(self.backgroundNode.view) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + func update( + context: AccountContext, + presentationData: PresentationData, + backgroundNode: WallpaperBackgroundNode?, + textString: NSAttributedString, + textInsets: UIEdgeInsets, + explicitBackgroundSize: CGSize?, + maxTextWidth: CGFloat, + effect: AvailableMessageEffects.MessageEffect?, + transition: Transition + ) -> CGSize { + var effectIconSize: CGSize? + if let effect { + let effectIcon: ComponentView + if let current = self.effectIcon { + effectIcon = current + } else { + effectIcon = ComponentView() + self.effectIcon = effectIcon + } + let effectIconContent: EffectIcon.Content + if let staticIcon = effect.staticIcon { + effectIconContent = .file(staticIcon) + } else { + effectIconContent = .text(effect.emoticon) + } + effectIconSize = effectIcon.update( + transition: .immediate, + component: AnyComponent(EffectIcon( + context: context, + content: effectIconContent + )), + environment: {}, + containerSize: CGSize(width: 8.0, height: 8.0) + ) + } + + var textCutout: TextNodeCutout? + if let effectIconSize { + textCutout = TextNodeCutout(bottomRight: CGSize(width: effectIconSize.width + 4.0, height: effectIconSize.height)) + } + + let textSize = self.text.update( + transition: .immediate, + component: AnyComponent(MultilineTextWithEntitiesComponent( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + placeholderColor: presentationData.theme.chat.message.stickerPlaceholderColor.withWallpaper, + text: .plain(textString), + maximumNumberOfLines: 0, + lineSpacing: 0.12, + cutout: textCutout, + insets: UIEdgeInsets() + )), + environment: {}, + containerSize: CGSize(width: maxTextWidth, height: 20000.0) + ) + + let size = CGSize(width: textSize.width + textInsets.left + textInsets.right, height: textSize.height + textInsets.top + textInsets.bottom) + + let textFrame = CGRect(origin: CGPoint(x: textInsets.left, y: textInsets.top), size: textSize) + if let textView = self.text.view { + if textView.superview == nil { + self.addSubview(textView) + } + textView.frame = textFrame + } + + let chatTheme: ChatPresentationThemeData + if let current = self.chatTheme, current.theme === presentationData.theme { + chatTheme = current + } else { + chatTheme = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper) + self.chatTheme = chatTheme + } + + let themeGraphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, bubbleCorners: presentationData.chatBubbleCorners) + self.backgroundWallpaperNode.setType( + type: .outgoing(.None), + theme: chatTheme, + essentialGraphics: themeGraphics, + maskMode: true, + backgroundNode: backgroundNode + ) + self.backgroundNode.setType( + type: .outgoing(.None), + highlighted: false, + graphics: themeGraphics, + maskMode: true, + hasWallpaper: true, + transition: transition.containedViewLayoutTransition, + backgroundNode: backgroundNode + ) + + let backgroundSize = explicitBackgroundSize ?? size + + let previousSize = self.currentSize + self.currentSize = backgroundSize + + if let effectIcon = self.effectIcon, let effectIconSize { + if let effectIconView = effectIcon.view { + var animateIn = false + if effectIconView.superview == nil { + animateIn = true + self.addSubview(effectIconView) + } + let effectIconFrame = CGRect(origin: CGPoint(x: backgroundSize.width - textInsets.right + 2.0 - effectIconSize.width, y: backgroundSize.height - textInsets.bottom - 2.0 - effectIconSize.height), size: effectIconSize) + if animateIn { + if let previousSize { + let previousEffectIconFrame = CGRect(origin: CGPoint(x: previousSize.width - textInsets.right + 2.0 - effectIconSize.width, y: previousSize.height - textInsets.bottom - 2.0 - effectIconSize.height), size: effectIconSize) + effectIconView.frame = previousEffectIconFrame + } else { + effectIconView.frame = effectIconFrame + } + transition.animateAlpha(view: effectIconView, from: 0.0, to: 1.0) + if !transition.animation.isImmediate { + effectIconView.layer.animateSpring(from: 0.001 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4) + } + } + + transition.setFrame(view: effectIconView, frame: effectIconFrame) + } + } else { + if let effectIcon = self.effectIcon { + self.effectIcon = nil + + if let effectIconView = effectIcon.view { + let effectIconSize = effectIconView.bounds.size + let effectIconFrame = CGRect(origin: CGPoint(x: backgroundSize.width - textInsets.right - effectIconSize.width, y: backgroundSize.height - textInsets.bottom - effectIconSize.height), size: effectIconSize) + transition.setFrame(view: effectIconView, frame: effectIconFrame) + transition.setScale(view: effectIconView, scale: 0.001) + transition.setAlpha(view: effectIconView, alpha: 0.0, completion: { [weak effectIconView] _ in + effectIconView?.removeFromSuperview() + }) + } + } + } + + let backgroundAlpha: CGFloat + if explicitBackgroundSize != nil { + backgroundAlpha = 0.0 + } else { + backgroundAlpha = 1.0 + } + + transition.setFrame(view: self.backgroundWallpaperNode.view, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + transition.setAlpha(view: self.backgroundWallpaperNode.view, alpha: backgroundAlpha) + self.backgroundWallpaperNode.updateFrame(CGRect(origin: CGPoint(), size: backgroundSize), transition: transition.containedViewLayoutTransition) + transition.setFrame(view: self.backgroundNode.view, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + transition.setAlpha(view: self.backgroundNode.view, alpha: backgroundAlpha) + self.backgroundNode.updateLayout(size: backgroundSize, transition: transition.containedViewLayoutTransition) + + return backgroundSize + } +} diff --git a/submodules/ContactListUI/Sources/ContactContextMenus.swift b/submodules/ContactListUI/Sources/ContactContextMenus.swift index 8e8815dc86..2a04b9af4c 100644 --- a/submodules/ContactListUI/Sources/ContactContextMenus.swift +++ b/submodules/ContactListUI/Sources/ContactContextMenus.swift @@ -34,7 +34,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con items.append(.action(ContextMenuActionItem(text: strings.StoryFeed_ContextOpenProfile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { let _ = (context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) ) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 593a022af4..f8dc911f7b 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -778,7 +778,7 @@ public class ContactsController: ViewController { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Contacts_AddContact, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let strongSelf = self else { return } @@ -789,7 +789,7 @@ public class ContactsController: ViewController { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Contacts_AddPeopleNearby, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Contact List/Context Menu/PeopleNearby"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let strongSelf = self else { return } diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index d58400ca17..ea6c74a2fc 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -101,11 +101,11 @@ public struct ContextMenuActionBadge: Equatable { public final class ContextMenuActionItem { public final class Action { - public let controller: ContextControllerProtocol + public let controller: ContextControllerProtocol? public let dismissWithResult: (ContextMenuActionResult) -> Void public let updateAction: (AnyHashable, ContextMenuActionItem) -> Void - init(controller: ContextControllerProtocol, dismissWithResult: @escaping (ContextMenuActionResult) -> Void, updateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void) { + init(controller: ContextControllerProtocol?, dismissWithResult: @escaping (ContextMenuActionResult) -> Void, updateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void) { self.controller = controller self.dismissWithResult = dismissWithResult self.updateAction = updateAction @@ -155,7 +155,7 @@ public final class ContextMenuActionItem { iconAnimation: IconAnimation? = nil, textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil }, textLinkAction: @escaping () -> Void = {}, - action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)? + action: ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)? ) { self.init( id: id, diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index ea8a437b13..7042758bc9 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -175,12 +175,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking } @objc private func pressed() { - guard let controller = self.getController() else { - return - } - self.item.action?(ContextMenuActionItem.Action( - controller: controller, + controller: self.getController(), dismissWithResult: { [weak self] result in guard let strongSelf = self else { return @@ -669,7 +665,7 @@ private final class ContextControllerActionsListCustomItemNode: ASDisplayNode, C } } -final class ContextControllerActionsListStackItem: ContextControllerActionsStackItem { +public final class ContextControllerActionsListStackItem: ContextControllerActionsStackItem { final class Node: ASDisplayNode, ContextControllerActionsStackItemNode { private final class Item { let node: ContextControllerActionsListItemNode @@ -933,14 +929,14 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack } } - let id: AnyHashable? - let items: [ContextMenuItem] - let reactionItems: ContextControllerReactionItems? - let tip: ContextController.Tip? - let tipSignal: Signal? - let dismissed: (() -> Void)? + public let id: AnyHashable? + public let items: [ContextMenuItem] + public let reactionItems: ContextControllerReactionItems? + public let tip: ContextController.Tip? + public let tipSignal: Signal? + public let dismissed: (() -> Void)? - init( + public init( id: AnyHashable?, items: [ContextMenuItem], reactionItems: ContextControllerReactionItems?, @@ -956,7 +952,7 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack self.dismissed = dismissed } - func node( + public func node( getController: @escaping () -> ContextControllerProtocol?, requestDismiss: @escaping (ContextMenuActionResult) -> Void, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, @@ -1094,8 +1090,8 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> [C } } -final class ContextControllerActionsStackNode: ASDisplayNode { - enum Presentation { +public final class ContextControllerActionsStackNode: ASDisplayNode { + public enum Presentation { case modal case inline case additional @@ -1372,19 +1368,19 @@ final class ContextControllerActionsStackNode: ASDisplayNode { private var selectionPanGesture: UIPanGestureRecognizer? - var topReactionItems: ContextControllerReactionItems? { + public var topReactionItems: ContextControllerReactionItems? { return self.itemContainers.last?.reactionItems } - var topPositionLock: CGFloat? { + public var topPositionLock: CGFloat? { return self.itemContainers.last?.positionLock } - var storedScrollingState: CGFloat? { + public var storedScrollingState: CGFloat? { return self.itemContainers.last?.storedScrollingState } - init( + public init( getController: @escaping () -> ContextControllerProtocol?, requestDismiss: @escaping (ContextMenuActionResult) -> Void, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void @@ -1434,7 +1430,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { } } - func replace(item: ContextControllerActionsStackItem, animated: Bool?) { + public func replace(item: ContextControllerActionsStackItem, animated: Bool?) { if let item = item as? ContextControllerActionsListStackItem, let topContainer = self.itemContainers.first, let topItem = topContainer.item as? ContextControllerActionsListStackItem, let topId = topItem.id, let id = item.id, topId == id, item.items.count == topItem.items.count { if let topNode = topContainer.node as? ContextControllerActionsListStackItem.Node { var matches = true @@ -1491,7 +1487,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { self.push(item: item, currentScrollingState: nil, positionLock: nil, animated: resolvedAnimated) } - func push(item: ContextControllerActionsStackItem, currentScrollingState: CGFloat?, positionLock: CGFloat?, animated: Bool) { + public func push(item: ContextControllerActionsStackItem, currentScrollingState: CGFloat?, positionLock: CGFloat?, animated: Bool) { if let itemContainer = self.itemContainers.last { itemContainer.storedScrollingState = currentScrollingState } @@ -1525,11 +1521,11 @@ final class ContextControllerActionsStackNode: ASDisplayNode { self.requestUpdate(transition) } - func clearStoredScrollingState() { + public func clearStoredScrollingState() { self.itemContainers.last?.storedScrollingState = nil } - func pop() { + public func pop() { if self.itemContainers.count == 1 { //dismiss } else { @@ -1546,7 +1542,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { self.requestUpdate(transition) } - func update( + public func update( presentationData: PresentationData, constrainedSize: CGSize, presentation: Presentation, @@ -1770,37 +1766,37 @@ final class ContextControllerActionsStackNode: ASDisplayNode { return CGSize(width: topItemWidth, height: topItemSize.height) } - func highlightGestureMoved(location: CGPoint) { + public func highlightGestureMoved(location: CGPoint) { if let topItemContainer = self.itemContainers.last { topItemContainer.highlightGestureMoved(location: self.view.convert(location, to: topItemContainer.view)) } } - func highlightGestureFinished(performAction: Bool) { + public func highlightGestureFinished(performAction: Bool) { if let topItemContainer = self.itemContainers.last { topItemContainer.highlightGestureFinished(performAction: performAction) } } - func decreaseHighlightedIndex() { + public func decreaseHighlightedIndex() { if let topItemContainer = self.itemContainers.last { topItemContainer.decreaseHighlightedIndex() } } - func increaseHighlightedIndex() { + public func increaseHighlightedIndex() { if let topItemContainer = self.itemContainers.last { topItemContainer.increaseHighlightedIndex() } } - func updatePanSelection(isEnabled: Bool) { + public func updatePanSelection(isEnabled: Bool) { if let selectionPanGesture = self.selectionPanGesture { selectionPanGesture.isEnabled = isEnabled } } - func animateIn() { + public func animateIn() { for itemContainer in self.itemContainers { if let tipNode = itemContainer.tipNode { tipNode.animateIn() diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 03b47bfce2..204ef30309 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -2503,11 +2503,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor) }, action: { c, _ in guard let strongSelf = self else { - c.dismiss(completion: nil) + c?.dismiss(completion: nil) return } - c.setItems(strongSelf.contextMenuSpeedItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuSpeedItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) items.append(.separator) @@ -2633,10 +2633,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, iconPosition: .left, action: { c, _ in guard let strongSelf = self else { - c.dismiss(completion: nil) + c?.dismiss(completion: nil) return } - c.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) let sliderValuePromise = ValuePromise(nil) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 1493695ac3..7a9bec72d8 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -433,6 +433,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { private var availableReactionsDisposable: Disposable? public let alwaysAllowPremiumReactions: Bool + private var hideExpandedTopPanel: Bool = false private var hasPremium: Bool? private var hasPremiumDisposable: Disposable? @@ -660,6 +661,8 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { return } + strongSelf.hideExpandedTopPanel = emojiContent.panelItemGroups.isEmpty + var emojiContent = emojiContent if let emojiSearchResult = emojiSearchState.result { var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? @@ -702,7 +705,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { var hideTopPanel = false if strongSelf.isReactionSearchActive { hideTopPanel = true - } else if strongSelf.alwaysAllowPremiumReactions { + } else if strongSelf.alwaysAllowPremiumReactions || strongSelf.hideExpandedTopPanel { hideTopPanel = true } @@ -717,7 +720,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { backgroundColor: .clear, separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), hideTopPanel: hideTopPanel, - disableTopPanel: strongSelf.alwaysAllowPremiumReactions, + disableTopPanel: strongSelf.alwaysAllowPremiumReactions || strongSelf.hideExpandedTopPanel, hideTopPanelUpdated: { hideTopPanel, transition in guard let strongSelf = self else { return @@ -1193,7 +1196,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { } var baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: self.contentTopInset + containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) if self.isExpanded { - if self.alwaysAllowPremiumReactions { + if self.alwaysAllowPremiumReactions || self.hideExpandedTopPanel { } else { baseNextFrame.origin.y += 46.0 + 54.0 - 4.0 } @@ -1322,7 +1325,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: actualBackgroundFrame.size) if self.isExpanded { - if self.alwaysAllowPremiumReactions { + if self.alwaysAllowPremiumReactions || self.hideExpandedTopPanel { scrollFrame.origin.y += 0.0 } else { scrollFrame.origin.y += 46.0 + 54.0 - 4.0 @@ -1347,7 +1350,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { self.updateScrolling(transition: transition) self.emojiContentLayout = EmojiPagerContentComponent.CustomLayout( - topPanelAlwaysHidden: self.alwaysAllowPremiumReactions, + topPanelAlwaysHidden: self.alwaysAllowPremiumReactions || self.hideExpandedTopPanel, itemsPerRow: itemCount, itemSize: itemSize, sideInset: sideInset, @@ -1377,7 +1380,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { var hideTopPanel = false if self.isReactionSearchActive { hideTopPanel = true - } else if self.alwaysAllowPremiumReactions { + } else if self.alwaysAllowPremiumReactions || self.hideExpandedTopPanel { hideTopPanel = true } @@ -1603,7 +1606,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { if !self.didInitializeEmojiContentHeight { self.didInitializeEmojiContentHeight = true - if emojiContent.contentItemGroups.count == 1 { + if emojiContent.contentItemGroups.count == 1 && emojiContent.contentItemGroups[0].title == nil { let itemCount = emojiContent.contentItemGroups[0].items.count let numRows = (itemCount + (emojiContentLayout.itemsPerRow - 1)) / emojiContentLayout.itemsPerRow let proposedHeight: CGFloat = CGFloat(numRows) * emojiContentLayout.itemSize + CGFloat(numRows - 1) * emojiContentLayout.itemSpacing + emojiContentLayout.itemSpacing * 2.0 + 5.0 @@ -2972,13 +2975,13 @@ public final class StandaloneReactionAnimation: ASDisplayNode { self.isUserInteractionEnabled = false } - public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) { - self.animateReactionSelection(context: context, theme: theme, animationCache: animationCache, reaction: reaction, avatarPeers: avatarPeers, playHaptic: playHaptic, isLarge: isLarge, playCenterReaction: playCenterReaction, forceSmallEffectAnimation: forceSmallEffectAnimation, hideCenterAnimation: hideCenterAnimation, targetView: targetView, addStandaloneReactionAnimation: addStandaloneReactionAnimation, currentItemNode: nil, completion: completion) + public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, customEffectResource: MediaResource? = nil, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) { + self.animateReactionSelection(context: context, theme: theme, animationCache: animationCache, reaction: reaction, customEffectResource: customEffectResource, avatarPeers: avatarPeers, playHaptic: playHaptic, isLarge: isLarge, playCenterReaction: playCenterReaction, forceSmallEffectAnimation: forceSmallEffectAnimation, hideCenterAnimation: hideCenterAnimation, targetView: targetView, addStandaloneReactionAnimation: addStandaloneReactionAnimation, currentItemNode: nil, completion: completion) } public var currentDismissAnimation: (() -> Void)? - public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) { + public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, customEffectResource: MediaResource? = nil, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) { guard let sourceSnapshotView = targetView.snapshotContentTree() else { completion() return @@ -3089,17 +3092,20 @@ public final class StandaloneReactionAnimation: ASDisplayNode { itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: isLarge, isPreviewing: false, transition: .immediate) } - let additionalAnimation: TelegramMediaFile? + var additionalAnimationResource: MediaResource? if isLarge && !forceSmallEffectAnimation { - additionalAnimation = reaction.largeApplicationAnimation + additionalAnimationResource = reaction.largeApplicationAnimation?.resource } else { - additionalAnimation = reaction.applicationAnimation + additionalAnimationResource = reaction.applicationAnimation?.resource + } + if additionalAnimationResource == nil, let customEffectResource { + additionalAnimationResource = customEffectResource } let additionalAnimationNode: AnimatedStickerNode? var genericAnimationView: AnimationView? - if let additionalAnimation = additionalAnimation { + if let additionalAnimationResource { let additionalAnimationNodeValue: AnimatedStickerNode if self.useDirectRendering { additionalAnimationNodeValue = DirectAnimatedStickerNode() @@ -3116,7 +3122,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode { let additionalCachePathPrefix: String? = nil - additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: context.account, resource: additionalAnimation.resource), width: Int(effectFrame.width * 1.33), height: Int(effectFrame.height * 1.33), playbackMode: .once, mode: .direct(cachePathPrefix: additionalCachePathPrefix)) + additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: context.account, resource: additionalAnimationResource), width: Int(effectFrame.width * 1.33), height: Int(effectFrame.height * 1.33), playbackMode: .once, mode: .direct(cachePathPrefix: additionalCachePathPrefix)) additionalAnimationNodeValue.frame = effectFrame additionalAnimationNodeValue.updateLayout(size: effectFrame.size) self.addSubnode(additionalAnimationNodeValue) diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift index a3eaabd3ed..4bbc99d2fe 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Combine.swift @@ -220,6 +220,12 @@ 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, _ s23: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23), 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), signalOfAny(s23)], 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, values[22] as! T23) + }, initialValues: [:], queue: queue) +} + public func combineLatest(queue: Queue? = nil, _ signals: [Signal]) -> Signal<[T], E> { if signals.count == 0 { return single([T](), E.self) diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index cb70fa94ea..207d40c1d9 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -567,7 +567,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }) }) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }))) @@ -615,14 +615,14 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }) })) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }) }))) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default) shareController.actionCompleted = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -633,7 +633,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }))) if !theme.theme.isDefault { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveTheme, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let actionSheet = ActionSheetController(presentationData: presentationData) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in @@ -682,7 +682,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme } else { items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_ChangeColors, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: reference, create: true)) pushControllerImpl?(controller) }) @@ -815,7 +815,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }) }) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }))) @@ -868,14 +868,14 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }) })) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }) }))) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default) shareController.actionCompleted = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -886,7 +886,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }))) if cloudThemeExists && !cloudTheme.theme.isDefault { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveTheme, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let actionSheet = ActionSheetController(presentationData: presentationData) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 3928a01f9d..5db9c88b6e 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -653,7 +653,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) }) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }))) @@ -701,14 +701,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) })) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }) }))) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default) shareController.actionCompleted = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -718,7 +718,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveTheme, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let actionSheet = ActionSheetController(presentationData: presentationData) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in @@ -766,7 +766,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The } else { items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_ChangeColors, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let controller = ThemeAccentColorController(context: context, mode: .colors(themeReference: reference, create: true)) pushControllerImpl?(controller) }) @@ -899,7 +899,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) }) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }))) @@ -952,14 +952,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) })) - c.dismiss(completion: { + c?.dismiss(completion: { pushControllerImpl?(controller) }) }) }))) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default) shareController.actionCompleted = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -970,7 +970,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }))) if cloudThemeExists { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveTheme, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { let actionSheet = ActionSheetController(presentationData: presentationData) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index af89fcc08b..222303780a 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -2024,7 +2024,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ViewInChannel, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer = peer else { diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index 11463ad99b..aacc3daa49 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -539,7 +539,7 @@ public func messageStatsController(context: AccountContext, updatedPresentationD } items.append(.action(ContextMenuActionItem(text: title, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: iconName), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer = peer, let navigationController = controller?.navigationController as? NavigationController else { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 5ff3bdd2ad..f227846ba4 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -570,7 +570,7 @@ private final class StickerPackContainer: ASDisplayNode { .action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c ,f in - c.popItems() + c?.popItems() })), .separator, .action(ContextMenuActionItem(text: self.presentationData.strings.Stickers_Delete_ForEveryone, textColor: .destructive, icon: { _ in return nil }, action: { [weak self] _ ,f in @@ -591,7 +591,7 @@ private final class StickerPackContainer: ASDisplayNode { } })) ] - c.pushItems(items: .single(ContextController.Items(content: .list(contextItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(contextItems)))) } }))) } @@ -1162,7 +1162,7 @@ private final class StickerPackContainer: ASDisplayNode { self?.togglePackInstalled() })) ] - c.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil, animated: true) + c?.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil, animated: true) } else { f(.default) self.presentDeletePack() diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index eb40aad7f3..1862a152b4 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -70,6 +70,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1163561432] = { return Api.AutoDownloadSettings.parse_autoDownloadSettings($0) } dict[-2124403385] = { return Api.AutoSaveException.parse_autoSaveException($0) } dict[-934791986] = { return Api.AutoSaveSettings.parse_autoSaveSettings($0) } + dict[-1815879042] = { return Api.AvailableEffect.parse_availableEffect($0) } dict[-1065882623] = { return Api.AvailableReaction.parse_availableReaction($0) } dict[-177732982] = { return Api.BankCardOpenUrl.parse_bankCardOpenUrl($0) } dict[1527845466] = { return Api.BaseTheme.parse_baseThemeArctic($0) } @@ -512,7 +513,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) } dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } - dict[592953125] = { return Api.Message.parse_message($0) } + dict[-1109353426] = { return Api.Message.parse_message($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[721967202] = { return Api.Message.parse_messageService($0) } dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) } @@ -928,7 +929,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-997782967] = { return Api.Update.parse_updateBotStopped($0) } dict[-2095595325] = { return Api.Update.parse_updateBotWebhookJSON($0) } dict[-1684914010] = { return Api.Update.parse_updateBotWebhookJSONQuery($0) } - dict[1550177112] = { return Api.Update.parse_updateBroadcastRevenueTransactions($0) } + dict[-539401739] = { return Api.Update.parse_updateBroadcastRevenueTransactions($0) } dict[1666927625] = { return Api.Update.parse_updateChannel($0) } dict[-1304443240] = { return Api.Update.parse_updateChannelAvailableMessages($0) } dict[-761649164] = { return Api.Update.parse_updateChannelMessageForwards($0) } @@ -1204,6 +1205,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-843329861] = { return Api.messages.AllStickers.parse_allStickers($0) } dict[-395967805] = { return Api.messages.AllStickers.parse_allStickersNotModified($0) } dict[1338747336] = { return Api.messages.ArchivedStickers.parse_archivedStickers($0) } + dict[-1109696146] = { return Api.messages.AvailableEffects.parse_availableEffects($0) } + dict[-772957605] = { return Api.messages.AvailableEffects.parse_availableEffectsNotModified($0) } dict[1989032621] = { return Api.messages.AvailableReactions.parse_availableReactions($0) } dict[-1626924713] = { return Api.messages.AvailableReactions.parse_availableReactionsNotModified($0) } dict[-347034123] = { return Api.messages.BotApp.parse_botApp($0) } @@ -1362,7 +1365,7 @@ public extension Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } @@ -1428,6 +1431,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.AutoSaveSettings: _1.serialize(buffer, boxed) + case let _1 as Api.AvailableEffect: + _1.serialize(buffer, boxed) case let _1 as Api.AvailableReaction: _1.serialize(buffer, boxed) case let _1 as Api.BankCardOpenUrl: @@ -2180,6 +2185,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.ArchivedStickers: _1.serialize(buffer, boxed) + case let _1 as Api.messages.AvailableEffects: + _1.serialize(buffer, boxed) case let _1 as Api.messages.AvailableReactions: _1.serialize(buffer, boxed) case let _1 as Api.messages.BotApp: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 0b3798ac9f..425a83a533 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -650,6 +650,62 @@ public extension Api { } } +public extension Api { + enum AvailableEffect: TypeConstructorDescription { + case availableEffect(flags: Int32, id: Int64, emoticon: String, staticIconId: Int64?, effectStickerId: Int64, effectAnimationId: Int64?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .availableEffect(let flags, let id, let emoticon, let staticIconId, let effectStickerId, let effectAnimationId): + if boxed { + buffer.appendInt32(-1815879042) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeString(emoticon, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(staticIconId!, buffer: buffer, boxed: false)} + serializeInt64(effectStickerId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt64(effectAnimationId!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .availableEffect(let flags, let id, let emoticon, let staticIconId, let effectStickerId, let effectAnimationId): + return ("availableEffect", [("flags", flags as Any), ("id", id as Any), ("emoticon", emoticon as Any), ("staticIconId", staticIconId as Any), ("effectStickerId", effectStickerId as Any), ("effectAnimationId", effectAnimationId as Any)]) + } + } + + public static func parse_availableEffect(_ reader: BufferReader) -> AvailableEffect? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } + var _5: Int64? + _5 = reader.readInt64() + var _6: Int64? + if Int(_1!) & Int(1 << 1) != 0 {_6 = reader.readInt64() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.AvailableEffect.availableEffect(flags: _1!, id: _2!, emoticon: _3!, staticIconId: _4, effectStickerId: _5!, effectAnimationId: _6) + } + else { + return nil + } + } + + } +} public extension Api { enum AvailableReaction: TypeConstructorDescription { case availableReaction(flags: Int32, reaction: String, title: String, staticIcon: Api.Document, appearAnimation: Api.Document, selectAnimation: Api.Document, activateAnimation: Api.Document, effectAnimation: Api.Document, aroundAnimation: Api.Document?, centerIcon: Api.Document?) @@ -1140,43 +1196,3 @@ public extension Api { } } -public extension Api { - enum BotCommand: TypeConstructorDescription { - case botCommand(command: String, description: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botCommand(let command, let description): - if boxed { - buffer.appendInt32(-1032140601) - } - serializeString(command, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botCommand(let command, let description): - return ("botCommand", [("command", command as Any), ("description", description as Any)]) - } - } - - public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotCommand.botCommand(command: _1!, description: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index d59ba867f9..a55f7e7a6f 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -628,15 +628,15 @@ public extension Api { } public extension Api { indirect enum Message: TypeConstructorDescription { - case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?) + case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?) case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?) case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, ttlPeriod: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId): + case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect): if boxed { - buffer.appendInt32(592953125) + buffer.appendInt32(-1109353426) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -672,6 +672,7 @@ public extension Api { }} if Int(flags) & Int(1 << 25) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 30) != 0 {serializeInt32(quickReplyShortcutId!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 2) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)} break case .messageEmpty(let flags, let id, let peerId): if boxed { @@ -699,8 +700,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId): - return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any)]) + case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect): + return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any)]) case .messageEmpty(let flags, let id, let peerId): return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)]) case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let ttlPeriod): @@ -783,6 +784,8 @@ public extension Api { if Int(_1!) & Int(1 << 25) != 0 {_25 = reader.readInt32() } var _26: Int32? if Int(_1!) & Int(1 << 30) != 0 {_26 = reader.readInt32() } + var _27: Int64? + if Int(_2!) & Int(1 << 2) != 0 {_27 = reader.readInt64() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -809,8 +812,9 @@ public extension Api { let _c24 = (Int(_1!) & Int(1 << 22) == 0) || _24 != nil let _c25 = (Int(_1!) & Int(1 << 25) == 0) || _25 != nil let _c26 = (Int(_1!) & Int(1 << 30) == 0) || _26 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 { - return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26) + let _c27 = (Int(_2!) & Int(1 << 2) == 0) || _27 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 { + return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 51d1fb1d7f..0880c7cd5a 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -1,3 +1,43 @@ +public extension Api { + enum BotCommand: TypeConstructorDescription { + case botCommand(command: String, description: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botCommand(let command, let description): + if boxed { + buffer.appendInt32(-1032140601) + } + serializeString(command, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botCommand(let command, let description): + return ("botCommand", [("command", command as Any), ("description", description as Any)]) + } + } + + public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.BotCommand.botCommand(command: _1!, description: _2!) + } + else { + return nil + } + } + + } +} public extension Api { indirect enum BotCommandScope: TypeConstructorDescription { case botCommandScopeChatAdmins @@ -1160,53 +1200,3 @@ public extension Api { } } -public extension Api { - enum BusinessIntro: TypeConstructorDescription { - case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .businessIntro(let flags, let title, let description, let sticker): - if boxed { - buffer.appendInt32(1510606445) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .businessIntro(let flags, let title, let description, let sticker): - return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)]) - } - } - - public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - var _4: Api.Document? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.Document - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index eb7a764d63..10a879a287 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -72,7 +72,7 @@ public extension Api { case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) case updateBotWebhookJSON(data: Api.DataJSON) case updateBotWebhookJSONQuery(queryId: Int64, data: Api.DataJSON, timeout: Int32) - case updateBroadcastRevenueTransactions(balances: Api.BroadcastRevenueBalances) + case updateBroadcastRevenueTransactions(peer: Api.Peer, balances: Api.BroadcastRevenueBalances) case updateChannel(channelId: Int64) case updateChannelAvailableMessages(channelId: Int64, availableMinId: Int32) case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32) @@ -396,10 +396,11 @@ public extension Api { data.serialize(buffer, true) serializeInt32(timeout, buffer: buffer, boxed: false) break - case .updateBroadcastRevenueTransactions(let balances): + case .updateBroadcastRevenueTransactions(let peer, let balances): if boxed { - buffer.appendInt32(1550177112) + buffer.appendInt32(-539401739) } + peer.serialize(buffer, true) balances.serialize(buffer, true) break case .updateChannel(let channelId): @@ -1413,8 +1414,8 @@ public extension Api { return ("updateBotWebhookJSON", [("data", data as Any)]) case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): return ("updateBotWebhookJSONQuery", [("queryId", queryId as Any), ("data", data as Any), ("timeout", timeout as Any)]) - case .updateBroadcastRevenueTransactions(let balances): - return ("updateBroadcastRevenueTransactions", [("balances", balances as Any)]) + case .updateBroadcastRevenueTransactions(let peer, let balances): + return ("updateBroadcastRevenueTransactions", [("peer", peer as Any), ("balances", balances as Any)]) case .updateChannel(let channelId): return ("updateChannel", [("channelId", channelId as Any)]) case .updateChannelAvailableMessages(let channelId, let availableMinId): @@ -2108,13 +2109,18 @@ public extension Api { } } public static func parse_updateBroadcastRevenueTransactions(_ reader: BufferReader) -> Update? { - var _1: Api.BroadcastRevenueBalances? + var _1: Api.Peer? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.BroadcastRevenueBalances + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.BroadcastRevenueBalances? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BroadcastRevenueBalances } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateBroadcastRevenueTransactions(balances: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateBroadcastRevenueTransactions(peer: _1!, balances: _2!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 79877efba2..fd1655b713 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -1164,6 +1164,74 @@ public extension Api.messages { } } +public extension Api.messages { + enum AvailableEffects: TypeConstructorDescription { + case availableEffects(hash: Int32, effects: [Api.AvailableEffect], documents: [Api.Document]) + case availableEffectsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .availableEffects(let hash, let effects, let documents): + if boxed { + buffer.appendInt32(-1109696146) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(effects.count)) + for item in effects { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + item.serialize(buffer, true) + } + break + case .availableEffectsNotModified: + if boxed { + buffer.appendInt32(-772957605) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .availableEffects(let hash, let effects, let documents): + return ("availableEffects", [("hash", hash as Any), ("effects", effects as Any), ("documents", documents as Any)]) + case .availableEffectsNotModified: + return ("availableEffectsNotModified", []) + } + } + + public static func parse_availableEffects(_ reader: BufferReader) -> AvailableEffects? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.AvailableEffect]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AvailableEffect.self) + } + var _3: [Api.Document]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.AvailableEffects.availableEffects(hash: _1!, effects: _2!, documents: _3!) + } + else { + return nil + } + } + public static func parse_availableEffectsNotModified(_ reader: BufferReader) -> AvailableEffects? { + return Api.messages.AvailableEffects.availableEffectsNotModified + } + + } +} public extension Api.messages { enum AvailableReactions: TypeConstructorDescription { case availableReactions(hash: Int32, reactions: [Api.AvailableReaction]) @@ -1222,45 +1290,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum BotApp: TypeConstructorDescription { - case botApp(flags: Int32, app: Api.BotApp) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botApp(let flags, let app): - if boxed { - buffer.appendInt32(-347034123) - } - serializeInt32(flags, buffer: buffer, boxed: false) - app.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botApp(let flags, let app): - return ("botApp", [("flags", flags as Any), ("app", app as Any)]) - } - } - - public static func parse_botApp(_ reader: BufferReader) -> BotApp? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.BotApp? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.BotApp - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.BotApp.botApp(flags: _1!, app: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 331ac6b496..3ec0b4d752 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -1,3 +1,53 @@ +public extension Api { + enum BusinessIntro: TypeConstructorDescription { + case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .businessIntro(let flags, let title, let description, let sticker): + if boxed { + buffer.appendInt32(1510606445) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .businessIntro(let flags, let title, let description, let sticker): + return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)]) + } + } + + public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: Api.Document? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Document + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) + } + else { + return nil + } + } + + } +} public extension Api { enum BusinessLocation: TypeConstructorDescription { case businessLocation(flags: Int32, geoPoint: Api.GeoPoint?, address: String) diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 6475bda11f..5b9410024d 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1,3 +1,45 @@ +public extension Api.messages { + enum BotApp: TypeConstructorDescription { + case botApp(flags: Int32, app: Api.BotApp) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botApp(let flags, let app): + if boxed { + buffer.appendInt32(-347034123) + } + serializeInt32(flags, buffer: buffer, boxed: false) + app.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botApp(let flags, let app): + return ("botApp", [("flags", flags as Any), ("app", app as Any)]) + } + } + + public static func parse_botApp(_ reader: BufferReader) -> BotApp? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.BotApp? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BotApp + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.BotApp.botApp(flags: _1!, app: _2!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum BotCallbackAnswer: TypeConstructorDescription { case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) @@ -1432,51 +1474,3 @@ public extension Api.messages { } } -public extension Api.messages { - indirect enum InvitedUsers: TypeConstructorDescription { - case invitedUsers(updates: Api.Updates, missingInvitees: [Api.MissingInvitee]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .invitedUsers(let updates, let missingInvitees): - if boxed { - buffer.appendInt32(2136862630) - } - updates.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingInvitees.count)) - for item in missingInvitees { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .invitedUsers(let updates, let missingInvitees): - return ("invitedUsers", [("updates", updates as Any), ("missingInvitees", missingInvitees as Any)]) - } - } - - public static func parse_invitedUsers(_ reader: BufferReader) -> InvitedUsers? { - var _1: Api.Updates? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Updates - } - var _2: [Api.MissingInvitee]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MissingInvitee.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.InvitedUsers.invitedUsers(updates: _1!, missingInvitees: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index 3f28e123a8..6cdc8ecbd8 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -1,3 +1,51 @@ +public extension Api.messages { + indirect enum InvitedUsers: TypeConstructorDescription { + case invitedUsers(updates: Api.Updates, missingInvitees: [Api.MissingInvitee]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .invitedUsers(let updates, let missingInvitees): + if boxed { + buffer.appendInt32(2136862630) + } + updates.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(missingInvitees.count)) + for item in missingInvitees { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .invitedUsers(let updates, let missingInvitees): + return ("invitedUsers", [("updates", updates as Any), ("missingInvitees", missingInvitees as Any)]) + } + } + + public static func parse_invitedUsers(_ reader: BufferReader) -> InvitedUsers? { + var _1: Api.Updates? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Updates + } + var _2: [Api.MissingInvitee]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MissingInvitee.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.InvitedUsers.invitedUsers(updates: _1!, missingInvitees: _2!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum MessageEditData: TypeConstructorDescription { case messageEditData(flags: Int32) diff --git a/submodules/TelegramApi/Sources/Api35.swift b/submodules/TelegramApi/Sources/Api35.swift index bc3083babe..e82f81fc69 100644 --- a/submodules/TelegramApi/Sources/Api35.swift +++ b/submodules/TelegramApi/Sources/Api35.swift @@ -2061,15 +2061,15 @@ public extension Api.functions.auth { } } public extension Api.functions.auth { - static func requestFirebaseSms(flags: Int32, phoneNumber: String, phoneCodeHash: String, safetyNetToken: String?, iosPushSecret: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func requestFirebaseSms(flags: Int32, phoneNumber: String, phoneCodeHash: String, playIntegrityToken: String?, iosPushSecret: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1991881904) + buffer.appendInt32(1940588736) serializeInt32(flags, buffer: buffer, boxed: false) serializeString(phoneNumber, buffer: buffer, boxed: false) serializeString(phoneCodeHash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(safetyNetToken!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeString(playIntegrityToken!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(iosPushSecret!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "auth.requestFirebaseSms", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("safetyNetToken", String(describing: safetyNetToken)), ("iosPushSecret", String(describing: iosPushSecret))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + return (FunctionDescription(name: "auth.requestFirebaseSms", parameters: [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("phoneCodeHash", String(describing: phoneCodeHash)), ("playIntegrityToken", String(describing: playIntegrityToken)), ("iosPushSecret", String(describing: iosPushSecret))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in let reader = BufferReader(buffer) var result: Api.Bool? if let signature = reader.readInt32() { @@ -5432,6 +5432,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getAvailableEffects(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-559805895) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getAvailableEffects", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AvailableEffects? in + let reader = BufferReader(buffer) + var result: Api.messages.AvailableEffects? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.AvailableEffects + } + return result + }) + } +} public extension Api.functions.messages { static func getAvailableReactions(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -7614,9 +7629,9 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(2077646913) + buffer.appendInt32(2018673486) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} @@ -7632,7 +7647,8 @@ public extension Api.functions.messages { if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 18) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -7643,9 +7659,9 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-537394132) + buffer.appendInt32(-1740662971) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} @@ -7660,7 +7676,8 @@ public extension Api.functions.messages { if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 18) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -7671,9 +7688,9 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(211175177) + buffer.appendInt32(934757205) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)} @@ -7685,7 +7702,8 @@ public extension Api.functions.messages { if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)} if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.sendMultiMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("multiMedia", String(describing: multiMedia)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 18) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.sendMultiMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("multiMedia", String(describing: multiMedia)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 49ef374575..d5844c0efd 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1704,7 +1704,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let strongSelf = self else { return } @@ -2517,7 +2517,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) items.append(.separator) break @@ -2550,7 +2550,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuAudioItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuAudioItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) } @@ -2587,7 +2587,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuPermissionItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuPermissionItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) } } @@ -2865,7 +2865,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) return .single(items) } @@ -2960,7 +2960,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) return items } @@ -3006,7 +3006,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController guard let strongSelf = self else { return } - c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + c?.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) }))) } return .single(items) diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index ad6225c9d6..15abf9236d 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -222,6 +222,7 @@ private var declaredEncodables: Void = { declareEncodable(DerivedDataMessageAttribute.self, f: { DerivedDataMessageAttribute(decoder: $0) }) declareEncodable(TelegramApplicationIcons.self, f: { TelegramApplicationIcons(decoder: $0) }) declareEncodable(OutgoingQuickReplyMessageAttribute.self, f: { OutgoingQuickReplyMessageAttribute(decoder: $0) }) + declareEncodable(EffectMessageAttribute.self, f: { EffectMessageAttribute(decoder: $0) }) return }() diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index d6b2e230e1..37817e5f03 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -126,7 +126,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { switch messsage { - case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let chatPeerId = messagePeerId return chatPeerId.peerId case let .messageEmpty(_, _, peerId): @@ -142,7 +142,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = chatPeerId.peerId var result = [peerId] @@ -266,7 +266,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? { switch message { - case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if let replyTo = replyTo { let peerId: PeerId = chatPeerId.peerId @@ -609,7 +609,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId): + case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId): let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId var namespace = namespace @@ -887,6 +887,10 @@ extension StoreMessage { if let restrictionReason = restrictionReason { attributes.append(RestrictedContentMessageAttribute(rules: restrictionReason.map(RestrictionRule.init(apiReason:)))) } + + if let messageEffectId { + attributes.append(EffectMessageAttribute(id: messageEffectId)) + } var storeFlags = StoreMessageFlags() diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 9bfc6e4754..38ca9941bc 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -133,7 +133,7 @@ private func sendFirebaseAuthorizationCode(accountManager: AccountManager mapError { _ -> SendFirebaseAuthorizationCodeError in return .generic } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index bc81f7ddd1..26de3f9e10 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -248,6 +248,8 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt return true case _ as WebpagePreviewMessageAttribute: return true + case _ as EffectMessageAttribute: + return true default: return false } diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index bfeb829948..7046de43f7 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -410,7 +410,7 @@ private func sendUploadedMessageContent( } } - sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil), info: .acknowledgement, tag: dependencyTag) + sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil), info: .acknowledgement, tag: dependencyTag) case let .media(inputMedia, text): if bubbleUpEmojiOrStickersets { flags |= Int32(1 << 15) @@ -432,7 +432,7 @@ private func sendUploadedMessageContent( } } - sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil), tag: dependencyTag) + sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil), tag: dependencyTag) |> map(NetworkRequestResult.result) case let .forward(sourceInfo): var topMsgId: Int32? @@ -633,7 +633,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M } } - sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil)) + sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil)) |> `catch` { _ -> Signal in return .complete() } @@ -651,7 +651,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M } } - sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil)) + sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil)) |> `catch` { _ -> Signal in return .complete() } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index efc40febf2..1aee0f5922 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1776,7 +1776,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: updatedState.updateNewAuthorization(isUnconfirmed: isUnconfirmed, hash: hash, date: date ?? 0, device: device ?? "", location: location ?? "") case let .updatePeerWallpaper(_, peer, wallpaper): updatedState.updateWallpaper(peerId: peer.peerId, wallpaper: wallpaper.flatMap { TelegramWallpaper(apiWallpaper: $0) }) - case let .updateBroadcastRevenueTransactions(balances): + case let .updateBroadcastRevenueTransactions(_, balances): updatedState.updateRevenueBalances(RevenueStats.Balances(apiRevenueBalances: balances)) default: break diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index 7f92d7df97..be1cbc0efc 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -90,6 +90,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(managedSynchronizeAvailableMessageEffects(postbox: self.stateManager.postbox, network: self.stateManager.network).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/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift index 99d113350c..d30668fb6a 100644 --- a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift @@ -96,7 +96,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var updatedTimestamp: Int32? if let apiMessage = apiMessage { switch apiMessage { - case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): updatedTimestamp = date case .messageEmpty: break diff --git a/submodules/TelegramCore/Sources/State/AvailableMessageEffects.swift b/submodules/TelegramCore/Sources/State/AvailableMessageEffects.swift new file mode 100644 index 0000000000..df2f76f248 --- /dev/null +++ b/submodules/TelegramCore/Sources/State/AvailableMessageEffects.swift @@ -0,0 +1,312 @@ +import Foundation +import TelegramApi +import Postbox +import SwiftSignalKit + +/* + availableEffect flags:# premium_required:flags.3?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:flags.1?long effect_animation_id:flags.2?long = AvailableEffect; + */ + +public final class AvailableMessageEffects: Equatable, Codable { + public final class MessageEffect: Equatable, Codable { + private enum CodingKeys: String, CodingKey { + case id + case isPremium + case emoticon + case staticIcon + case effectSticker + case effectAnimation + } + + public let id: Int64 + public let isPremium: Bool + public let emoticon: String + public let staticIcon: TelegramMediaFile? + public let effectSticker: TelegramMediaFile + public let effectAnimation: TelegramMediaFile? + + public init( + id: Int64, + isPremium: Bool, + emoticon: String, + staticIcon: TelegramMediaFile?, + effectSticker: TelegramMediaFile, + effectAnimation: TelegramMediaFile? + ) { + self.id = id + self.isPremium = isPremium + self.emoticon = emoticon + self.staticIcon = staticIcon + self.effectSticker = effectSticker + self.effectAnimation = effectAnimation + } + + public static func ==(lhs: MessageEffect, rhs: MessageEffect) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.isPremium != rhs.isPremium { + return false + } + if lhs.emoticon != rhs.emoticon { + return false + } + if lhs.staticIcon != rhs.staticIcon { + return false + } + if lhs.effectSticker != rhs.effectSticker { + return false + } + if lhs.effectAnimation != rhs.effectAnimation { + return false + } + return true + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.id = try container.decode(Int64.self, forKey: .id) + self.isPremium = try container.decodeIfPresent(Bool.self, forKey: .isPremium) ?? false + self.emoticon = try container.decode(String.self, forKey: .emoticon) + + if let staticIconData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .staticIcon) { + self.staticIcon = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: staticIconData.data))) + } else { + self.staticIcon = nil + } + + do { + let effectStickerData = try container.decode(AdaptedPostboxDecoder.RawObjectData.self, forKey: .effectSticker) + self.effectSticker = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectStickerData.data))) + } + + if let effectAnimationData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: .effectAnimation) { + self.effectAnimation = TelegramMediaFile(decoder: PostboxDecoder(buffer: MemoryBuffer(data: effectAnimationData.data))) + } else { + self.effectAnimation = nil + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.id, forKey: .id) + try container.encode(self.emoticon, forKey: .emoticon) + try container.encode(self.isPremium, forKey: .isPremium) + + if let staticIcon = self.staticIcon { + try container.encode(PostboxEncoder().encodeObjectToRawData(staticIcon), forKey: .staticIcon) + } + try container.encode(PostboxEncoder().encodeObjectToRawData(self.effectSticker), forKey: .effectSticker) + if let effectAnimation = self.effectAnimation { + try container.encode(PostboxEncoder().encodeObjectToRawData(effectAnimation), forKey: .effectAnimation) + } + } + } + + private enum CodingKeys: String, CodingKey { + case newHash + case messageEffects + } + + public let hash: Int32 + public let messageEffects: [MessageEffect] + + public init( + hash: Int32, + messageEffects: [MessageEffect] + ) { + self.hash = hash + self.messageEffects = messageEffects + } + + public static func ==(lhs: AvailableMessageEffects, rhs: AvailableMessageEffects) -> Bool { + if lhs.hash != rhs.hash { + return false + } + if lhs.messageEffects != rhs.messageEffects { + return false + } + return true + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.hash = try container.decodeIfPresent(Int32.self, forKey: .newHash) ?? 0 + self.messageEffects = try container.decode([MessageEffect].self, forKey: .messageEffects) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.hash, forKey: .newHash) + try container.encode(self.messageEffects, forKey: .messageEffects) + } +} + +//availableEffect flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect; + +private extension AvailableMessageEffects.MessageEffect { + convenience init?(apiMessageEffect: Api.AvailableEffect, files: [Int64: TelegramMediaFile]) { + switch apiMessageEffect { + case let .availableEffect(flags, id, emoticon, staticIconId, effectStickerId, effectAnimationId): + guard let effectSticker = files[effectStickerId] else { + return nil + } + + let isPremium = (flags & (1 << 3)) != 0 + self.init( + id: id, + isPremium: isPremium, + emoticon: emoticon, + staticIcon: staticIconId.flatMap({ files[$0] }), + effectSticker: effectSticker, + effectAnimation: effectAnimationId.flatMap({ files[$0] }) + ) + } + } +} + +func _internal_cachedAvailableMessageEffects(postbox: Postbox) -> Signal { + return postbox.transaction { transaction -> AvailableMessageEffects? in + return _internal_cachedAvailableMessageEffects(transaction: transaction) + } +} + +func _internal_cachedAvailableMessageEffects(transaction: Transaction) -> AvailableMessageEffects? { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) + + let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.availableMessageEffects, key: key))?.get(AvailableMessageEffects.self) + if let cached = cached { + return cached + } else { + return nil + } +} + +func _internal_setCachedAvailableMessageEffects(transaction: Transaction, availableMessageEffects: AvailableMessageEffects) { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) + + if let entry = CodableEntry(availableMessageEffects) { + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.availableMessageEffects, key: key), entry: entry) + } +} + +func managedSynchronizeAvailableMessageEffects(postbox: Postbox, network: Network) -> Signal { + let poll = Signal { subscriber in + let signal: Signal = _internal_cachedAvailableMessageEffects(postbox: postbox) + |> mapToSignal { current in + let sourceHash: Int32 + #if DEBUG + sourceHash = 0 + #else + sourceHash = current?.hash ?? 0 + #endif + return (network.request(Api.functions.messages.getAvailableEffects(hash: sourceHash)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Signal in + guard let result = result else { + return .complete() + } + switch result { + case let .availableEffects(hash, effects, documents): + var files: [Int64: TelegramMediaFile] = [:] + for document in documents { + if let file = telegramMediaFileFromApiDocument(document) { + files[file.fileId.id] = file + } + } + + var parsedEffects: [AvailableMessageEffects.MessageEffect] = [] + for effect in effects { + if let parsedEffect = AvailableMessageEffects.MessageEffect(apiMessageEffect: effect, files: files) { + parsedEffects.append(parsedEffect) + } + } + _internal_setCachedAvailableMessageEffects(transaction: transaction, availableMessageEffects: AvailableMessageEffects( + hash: hash, + messageEffects: parsedEffects + )) + case .availableEffectsNotModified: + break + } + + var signals: [Signal] = [] + + if let availableMessageEffects = _internal_cachedAvailableMessageEffects(transaction: transaction) { + var resources: [MediaResource] = [] + + for messageEffect in availableMessageEffects.messageEffects { + if let staticIcon = messageEffect.staticIcon { + resources.append(staticIcon.resource) + } + if messageEffect.effectSticker.isPremiumSticker { + if let effectFile = messageEffect.effectSticker.videoThumbnails.first { + resources.append(effectFile.resource) + } + } else { + if let effectAnimation = messageEffect.effectAnimation { + resources.append(effectAnimation.resource) + } + } + } + + for resource in resources { + signals.append( + fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .other, reference: .standalone(resource: resource)) + |> ignoreValues + |> `catch` { _ -> Signal in + return .complete() + } + ) + } + } + + return combineLatest(signals) + |> ignoreValues + } + |> switchToLatest + }) + } + + return signal.start(completed: { + subscriber.putCompletion() + }) + } + + return ( + poll + |> then( + .complete() + |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()) + ) + ) + |> restart +} + +public extension Message { + func messageEffect(availableMessageEffects: AvailableMessageEffects?) -> AvailableMessageEffects.MessageEffect? { + guard let availableMessageEffects else { + return nil + } + for attribute in self.attributes { + if let attribute = attribute as? EffectMessageAttribute { + for effect in availableMessageEffects.messageEffects { + if effect.id == attribute.id { + return effect + } + } + break + } + } + return nil + } +} diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 8ed18226d6..f67e5f9529 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -794,6 +794,7 @@ public final class PendingMessageManager { var scheduleTime: Int32? var sendAsPeerId: PeerId? var quickReply: OutgoingQuickReplyMessageAttribute? + var messageEffect: EffectMessageAttribute? var flags: Int32 = 0 @@ -824,6 +825,8 @@ public final class PendingMessageManager { sendAsPeerId = attribute.peerId } else if let attribute = attribute as? OutgoingQuickReplyMessageAttribute { quickReply = attribute + } else if let attribute = attribute as? EffectMessageAttribute { + messageEffect = attribute } } @@ -1016,7 +1019,13 @@ public final class PendingMessageManager { flags |= 1 << 17 } - sendMessageRequest = network.request(Api.functions.messages.sendMultiMedia(flags: flags, peer: inputPeer, replyTo: replyTo, multiMedia: singleMedias, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut)) + var messageEffectId: Int64? + if let messageEffect { + flags |= 1 << 18 + messageEffectId = messageEffect.id + } + + sendMessageRequest = network.request(Api.functions.messages.sendMultiMedia(flags: flags, peer: inputPeer, replyTo: replyTo, multiMedia: singleMedias, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId)) } return sendMessageRequest @@ -1192,6 +1201,7 @@ public final class PendingMessageManager { var sendAsPeerId: PeerId? var bubbleUpEmojiOrStickersets = false var quickReply: OutgoingQuickReplyMessageAttribute? + var messageEffect: EffectMessageAttribute? var flags: Int32 = 0 @@ -1228,6 +1238,8 @@ public final class PendingMessageManager { sendAsPeerId = attribute.peerId } else if let attribute = attribute as? OutgoingQuickReplyMessageAttribute { quickReply = attribute + } else if let attribute = attribute as? EffectMessageAttribute { + messageEffect = attribute } } @@ -1327,7 +1339,13 @@ public final class PendingMessageManager { flags |= 1 << 17 } - sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: message.text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut), info: .acknowledgement, tag: dependencyTag) + var messageEffectId: Int64? + if let messageEffect { + flags |= 1 << 18 + messageEffectId = messageEffect.id + } + + sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: message.text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId), info: .acknowledgement, tag: dependencyTag) case let .media(inputMedia, text): if bubbleUpEmojiOrStickersets { flags |= Int32(1 << 15) @@ -1401,8 +1419,14 @@ public final class PendingMessageManager { } flags |= 1 << 17 } + + var messageEffectId: Int64? + if let messageEffect { + flags |= 1 << 18 + messageEffectId = messageEffect.id + } - sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut), tag: dependencyTag) + sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId), tag: dependencyTag) |> map(NetworkRequestResult.result) case let .forward(sourceInfo): var topMsgId: Int32? diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index e133e4f83e..6b263c5b05 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 179 + return 180 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift index 117c3ea820..09276f544e 100644 --- a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift @@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod): - let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil) + let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { @@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService { let generatedPeerId = Api.Peer.peerUser(userId: userId) - let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil) + let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index 93b58ad893..1cb21c63b5 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -104,7 +104,7 @@ extension Api.MessageMedia { extension Api.Message { var rawId: Int32 { switch self { - case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return id case let .messageEmpty(_, id, _): return id @@ -115,7 +115,7 @@ extension Api.Message { func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? { switch self { - case let .message(_, _, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = messagePeerId.peerId return MessageId(peerId: peerId, namespace: namespace, id: id) case let .messageEmpty(_, id, peerId): @@ -132,7 +132,7 @@ extension Api.Message { var peerId: PeerId? { switch self { - case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = messagePeerId.peerId return peerId case let .messageEmpty(_, _, peerId): @@ -145,7 +145,7 @@ extension Api.Message { var timestamp: Int32? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return date case let .messageService(_, _, _, _, _, date, _, _): return date @@ -156,7 +156,7 @@ extension Api.Message { var preCachedResources: [(MediaResource, Data)]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedResources default: return nil @@ -165,7 +165,7 @@ extension Api.Message { var preCachedStories: [StoryId: Api.StoryItem]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedStories default: return nil diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_EffectMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_EffectMessageAttribute.swift new file mode 100644 index 0000000000..a372696a49 --- /dev/null +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_EffectMessageAttribute.swift @@ -0,0 +1,18 @@ +import Foundation +import Postbox + +public class EffectMessageAttribute: MessageAttribute { + public let id: Int64 + + public init(id: Int64) { + self.id = id + } + + required public init(decoder: PostboxDecoder) { + self.id = decoder.decodeInt64ForKey("id", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt64(self.id, forKey: "id") + } +} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index ce131e8859..4c07120c5e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -125,6 +125,7 @@ public struct Namespaces { public static let peerColorOptions: Int8 = 34 public static let savedMessageTags: Int8 = 35 public static let applicationIcons: Int8 = 36 + public static let availableMessageEffects: Int8 = 37 } public struct UnorderedItemList { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 42ee3b92fa..a3cf7bfac5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -145,6 +145,10 @@ public extension TelegramEngine { return _internal_cachedAvailableReactions(postbox: self.account.postbox) } + public func availableMessageEffects() -> Signal { + return _internal_cachedAvailableMessageEffects(postbox: self.account.postbox) + } + public func savedMessageTagData() -> Signal { return self.account.postbox.combinedView(keys: [PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())]) |> mapToSignal { views -> Signal in diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index c557f38de8..dd52c9c45a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1045,6 +1045,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 58a9ed5d87..be15aeecee 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -724,6 +724,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), + messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, @@ -1711,6 +1712,24 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { return nil } + public func messageEffectTargetView() -> UIView? { + if let statusNode = self.statusNode, !statusNode.isHidden { + if let result = statusNode.messageEffectTargetView() { + return result + } + } + if let result = self.contentFile?.dateAndStatusNode.messageEffectTargetView() { + return result + } + if let result = self.contentMedia?.dateAndStatusNode.messageEffectTargetView() { + return result + } + if let result = self.contentInstantVideo?.dateAndStatusNode.messageEffectTargetView() { + return result + } + return nil + } + public func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? { return self.contentMedia?.playMediaWithSound() } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift index 59a6d7a543..c34766a424 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift @@ -295,6 +295,10 @@ open class ChatMessageBubbleContentNode: ASDisplayNode { return nil } + open func messageEffectTargetView() -> UIView? { + return nil + } + open func targetForStoryTransition(id: StoryId) -> UIView? { return nil } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD index bcba245fc1..0dfda1903f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD @@ -81,6 +81,9 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode", "//submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode", "//submodules/UIKitRuntimeUtils", + "//submodules/TelegramUI/Components/Chat/ChatMessageTransitionNode", + "//submodules/AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 51ef9fca48..ecd72210c7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -71,6 +71,9 @@ import ChatMessageGiftBubbleContentNode import ChatMessageGiveawayBubbleContentNode import ChatMessageJoinedChannelBubbleContentNode import UIKitRuntimeUtils +import ChatMessageTransitionNode +import AnimatedStickerNode +import TelegramAnimatedStickerNode private struct BubbleItemAttributes { var isAttachment: Bool @@ -601,6 +604,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var appliedForwardInfo: (Peer?, String?)? private var disablesComments = true + private var wasPending: Bool = false + private var didChangeFromPendingToSent: Bool = false + private var authorNameColor: UIColor? private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer? @@ -641,6 +647,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI containerSize: credibilityIconView.bounds.size ) } + + self.updateVisibility() } } } @@ -1152,9 +1160,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return .waitForSingleTap } } + if !strongSelf.backgroundNode.frame.contains(point) { return .waitForDoubleTap } + + if strongSelf.currentMessageEffect() != nil { + return .waitForDoubleTap + } } return .waitForDoubleTap @@ -2161,6 +2174,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, @@ -2998,6 +3012,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return } + if item.message.id.namespace == Namespaces.Message.Local || item.message.id.namespace == Namespaces.Message.ScheduledLocal || item.message.id.namespace == Namespaces.Message.QuickReplyLocal { + strongSelf.wasPending = true + } + if strongSelf.wasPending && (item.message.id.namespace != Namespaces.Message.Local && item.message.id.namespace != Namespaces.Message.ScheduledLocal && item.message.id.namespace != Namespaces.Message.QuickReplyLocal) { + strongSelf.didChangeFromPendingToSent = true + } + let themeUpdated = strongSelf.appliedItem?.presentationData.theme.theme !== item.presentationData.theme.theme let previousContextFrame = strongSelf.mainContainerNode.frame strongSelf.mainContainerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize) @@ -4216,6 +4237,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.updateSearchTextHighlightState() + strongSelf.updateVisibility() + if let (_, f) = strongSelf.awaitingAppliedReaction { strongSelf.awaitingAppliedReaction = nil @@ -4652,6 +4675,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } } + if self.currentMessageEffect() != nil { + if self.backgroundNode.frame.contains(location) { + return .action(InternalBubbleTapAction.Action({ [weak self] in + guard let self else { + return + } + self.playMessageEffect(force: true) + }, contextMenuOnLongPress: true)) + } + } return nil case .longTap, .doubleTap, .secondaryTap: if let item = self.item, self.backgroundNode.frame.contains(location) { @@ -5541,6 +5574,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for contentNode in self.contentNodes { contentNode.unreadMessageRangeUpdated() } + + self.updateVisibility() } public func animateQuizInvalidOptionSelected() { @@ -5726,4 +5761,178 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } return false } + + private var forceStopAnimations: Bool = false + private var playedPremiumStickerAnimation: Bool = false + private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = [] + + private func playPremiumStickerAnimation(effect: AvailableMessageEffects.MessageEffect, force: Bool) { + if self.playedPremiumStickerAnimation && !force { + return + } + self.playedPremiumStickerAnimation = true + + if let effectAnimation = effect.effectAnimation { + self.playEffectAnimation(resource: effectAnimation.resource, isStickerEffect: true) + } else { + let effectSticker = effect.effectSticker + if let effectFile = effectSticker.videoThumbnails.first { + self.playEffectAnimation(resource: effectFile.resource, isStickerEffect: true) + } + } + } + + private func playEffectAnimation(resource: MediaResource, isStickerEffect: Bool = false) { + guard let item = self.item else { + return + } + guard let transitionNode = item.controllerInteraction.getMessageTransitionNode() as? ChatMessageTransitionNode else { + return + } + + let source = AnimatedStickerResourceSource(account: item.context.account, resource: resource, fitzModifier: nil) + + let animationSize = CGSize(width: 180.0, height: 180.0) + let animationNodeFrame: CGRect + + var messageEffectView: UIView? + for contentNode in self.contentNodes { + if let result = contentNode.messageEffectTargetView() { + messageEffectView = result + break + } + } + if messageEffectView == nil { + if let mosaicStatusNode = self.mosaicStatusNode, let result = mosaicStatusNode.messageEffectTargetView() { + messageEffectView = result + } + } + + if let messageEffectView { + animationNodeFrame = animationSize.centered(around: messageEffectView.convert(messageEffectView.bounds, to: self.view).center) + } else { + animationNodeFrame = animationSize.centered(around: self.backgroundNode.frame.center) + } + + if self.additionalAnimationNodes.count >= 4 { + return + } + + let incomingMessage = item.message.effectivelyIncoming(item.context.account.peerId) + + do { + let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id) + let additionalAnimationNode = DefaultAnimatedStickerNodeImpl() + additionalAnimationNode.setup(source: source, width: Int(animationSize.width * 1.6), height: Int(animationSize.height * 1.6), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix)) + var animationFrame: CGRect + if isStickerEffect { + let scale: CGFloat = 0.245 + let offsetScale: CGFloat = 0.5 + animationFrame = animationNodeFrame.offsetBy(dx: incomingMessage ? animationNodeFrame.width * offsetScale : -animationNodeFrame.width * offsetScale, dy: -25.0).insetBy(dx: -animationNodeFrame.width * scale, dy: -animationNodeFrame.height * scale) + } else { + animationFrame = animationNodeFrame.insetBy(dx: -animationNodeFrame.width, dy: -animationNodeFrame.height) + .offsetBy(dx: incomingMessage ? animationNodeFrame.width - 10.0 : -animationNodeFrame.width + 10.0, dy: 0.0) + animationFrame = animationFrame.offsetBy(dx: CGFloat.random(in: -30.0 ... 30.0), dy: CGFloat.random(in: -30.0 ... 30.0)) + } + + animationFrame = animationFrame.offsetBy(dx: 0.0, dy: self.insets.top) + additionalAnimationNode.frame = animationFrame + if incomingMessage { + additionalAnimationNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + } + + let decorationNode = transitionNode.add(decorationView: additionalAnimationNode.view, itemNode: self) + additionalAnimationNode.completed = { [weak self, weak decorationNode, weak transitionNode] _ in + guard let decorationNode = decorationNode else { + return + } + self?.additionalAnimationNodes.removeAll(where: { $0 === decorationNode }) + transitionNode?.remove(decorationNode: decorationNode) + } + additionalAnimationNode.isPlayingChanged = { [weak self, weak decorationNode, weak transitionNode] isPlaying in + if !isPlaying { + guard let decorationNode = decorationNode else { + return + } + self?.additionalAnimationNodes.removeAll(where: { $0 === decorationNode }) + transitionNode?.remove(decorationNode: decorationNode) + } + } + + self.additionalAnimationNodes.append(decorationNode) + + additionalAnimationNode.visibility = true + } + } + + private func removeAdditionalAnimations() { + for decorationNode in self.additionalAnimationNodes { + if let additionalAnimationNode = decorationNode.contentView.asyncdisplaykit_node as? AnimatedStickerNode { + additionalAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak additionalAnimationNode] _ in + additionalAnimationNode?.visibility = false + }) + } + } + } + + private func currentMessageEffect() -> AvailableMessageEffects.MessageEffect? { + guard let item = self.item else { + return nil + } + var messageEffect: AvailableMessageEffects.MessageEffect? + for attribute in item.message.attributes { + if let attribute = attribute as? EffectMessageAttribute { + if let availableMessageEffects = item.associatedData.availableMessageEffects { + for effect in availableMessageEffects.messageEffects { + if effect.id == attribute.id { + messageEffect = effect + break + } + } + } + break + } + } + return messageEffect + } + + private func playMessageEffect(force: Bool) { + if let messageEffect = self.currentMessageEffect() { + self.playPremiumStickerAnimation(effect: messageEffect, force: force) + } + } + + private func updateVisibility() { + guard let item = self.item else { + return + } + + let isPlaying = self.visibilityStatus == true && !self.forceStopAnimations + if !isPlaying { + self.removeAdditionalAnimations() + } + + var alreadySeen = true + if item.message.flags.contains(.Incoming) { + if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] { + if unreadRange.contains(item.message.id.id) { + if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) { + alreadySeen = false + } + } + } + } else { + if self.didChangeFromPendingToSent { + if !item.controllerInteraction.seenOneTimeAnimatedMedia.contains(item.message.id) { + alreadySeen = false + } + } + } + + if !alreadySeen { + item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id) + + self.playMessageEffect(force: false) + } + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index f94d9c3786..412b08e962 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -295,6 +295,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -573,6 +574,13 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { return nil } + override public func messageEffectTargetView() -> UIView? { + if !self.dateAndStatusNode.isHidden { + return self.dateAndStatusNode.messageEffectTargetView() + } + return nil + } + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.dateAndStatusNode.supernode != nil, let result = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: event) { return result diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 91e50ef149..d0e62a3596 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -59,6 +59,8 @@ private final class StatusReactionNode: ASDisplayNode { private var resolvedFile: TelegramMediaFile? private var fileDisposable: Disposable? + private var alternativeTextView: ImmediateTextView? + override init() { self.iconView = ReactionIconView() @@ -72,66 +74,11 @@ private final class StatusReactionNode: ASDisplayNode { self.fileDisposable?.dispose() } - func update(context: AccountContext, type: ChatMessageDateAndStatusType, value: MessageReaction.Reaction, file: TelegramMediaFile?, fileId: Int64?, isSelected: Bool, count: Int, theme: PresentationTheme, wallpaper: TelegramWallpaper, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, animated: Bool) { + func update(context: AccountContext, type: ChatMessageDateAndStatusType, value: MessageReaction.Reaction, file: TelegramMediaFile?, fileId: Int64?, alternativeText: String, isSelected: Bool, count: Int, theme: PresentationTheme, wallpaper: TelegramWallpaper, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, animated: Bool) { if self.value != value { self.value = value - let boundingImageSize = CGSize(width: 14.0, height: 14.0) - /*let defaultImageSize = CGSize(width: boundingImageSize.width + floor(boundingImageSize.width * 0.5 * 2.0), height: boundingImageSize.height + floor(boundingImageSize.height * 0.5 * 2.0)) - let imageSize: CGSize - if let file = file { - self.iconImageDisposable.set((reactionStaticImage(context: context, animation: file, pixelSize: CGSize(width: 72.0, height: 72.0), queue: sharedReactionStaticImage) - |> deliverOnMainQueue).start(next: { [weak self] data in - guard let strongSelf = self else { - return - } - - if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { - if let image = UIImage(data: dataValue) { - strongSelf.iconView.imageView.image = image - } - } - })) - imageSize = file.dimensions?.cgSize.aspectFitted(defaultImageSize) ?? defaultImageSize - - self.fileDisposable?.dispose() - self.fileDisposable = nil - } else if let fileId = fileId { - self.fileDisposable?.dispose() - self.fileDisposable = nil - - imageSize = boundingImageSize - - if let resolvedFile = self.resolvedFile, resolvedFile.fileId.id == fileId { - } else { - self.fileDisposable = (context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) - |> deliverOnMainQueue).start(next: { [weak self] result in - guard let strongSelf = self, let file = result[fileId] else { - return - } - - strongSelf.resolvedFile = file - - strongSelf.iconImageDisposable.set((reactionStaticImage(context: context, animation: file, pixelSize: CGSize(width: 72.0, height: 72.0), queue: sharedReactionStaticImage) - |> deliverOnMainQueue).start(next: { data in - guard let strongSelf = self else { - return - } - - if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { - if let image = UIImage(data: dataValue) { - strongSelf.iconView.imageView.image = image - } - } - })) - }) - } - } else { - imageSize = defaultImageSize - - self.fileDisposable?.dispose() - self.fileDisposable = nil - }*/ + let boundingImageSize = CGSize(width: 8.0, height: 8.0) let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingImageSize.width - boundingImageSize.width) / 2.0), y: floorToScreenPixels((boundingImageSize.height - boundingImageSize.height) / 2.0)), size: boundingImageSize) self.iconView.frame = iconFrame @@ -172,6 +119,21 @@ private final class StatusReactionNode: ASDisplayNode { reaction: value, transition: .immediate ) + if let alternativeTextView = self.alternativeTextView { + self.alternativeTextView = nil + alternativeTextView.removeFromSuperview() + } + } else { + let alternativeTextView: ImmediateTextView + if let current = self.alternativeTextView { + alternativeTextView = current + } else { + alternativeTextView = ImmediateTextView() + self.view.addSubview(alternativeTextView) + } + alternativeTextView.attributedText = NSAttributedString(string: alternativeText, font: Font.regular(10.0), textColor: .black) + let alternativeTextSize = alternativeTextView.updateLayout(CGSize(width: 100.0, height: 100.0)) + alternativeTextView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingImageSize.width - alternativeTextSize.width) / 2.0), y: floorToScreenPixels((boundingImageSize.height - alternativeTextSize.height) / 2.0)), size: alternativeTextSize) } } } @@ -230,6 +192,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var reactionPeers: [(MessageReaction.Reaction, EnginePeer)] var displayAllReactionPeers: Bool var areReactionsTags: Bool + var messageEffect: AvailableMessageEffects.MessageEffect? var replyCount: Int var isPinned: Bool var hasAutoremove: Bool @@ -252,6 +215,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { reactionPeers: [(MessageReaction.Reaction, EnginePeer)], displayAllReactionPeers: Bool, areReactionsTags: Bool, + messageEffect: AvailableMessageEffects.MessageEffect?, replyCount: Int, isPinned: Bool, hasAutoremove: Bool, @@ -273,6 +237,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { self.reactionPeers = reactionPeers self.displayAllReactionPeers = displayAllReactionPeers self.areReactionsTags = areReactionsTags + self.messageEffect = messageEffect self.replyCount = replyCount self.isPinned = isPinned self.hasAutoremove = hasAutoremove @@ -292,7 +257,6 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { private var impressionIcon: ASImageNode? private var reactionNodes: [MessageReaction.Reaction: StatusReactionNode] = [:] private let reactionButtonsContainer = ReactionButtonsAsyncLayoutContainer() - private var reactionCountNode: TextNode? private var reactionButtonNode: HighlightTrackingButtonNode? private var repliesIcon: ASImageNode? private var selfExpiringIcon: ASImageNode? @@ -356,7 +320,6 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { let currentTheme = self.theme let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode) - let makeReactionCountLayout = TextNode.asyncLayout(self.reactionCountNode) let reactionButtonsContainer = self.reactionButtonsContainer @@ -679,35 +642,11 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? - let reactionSize: CGFloat = 17.0 - var reactionCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? + let reactionSize: CGFloat = 8.0 let reactionSpacing: CGFloat = 2.0 let reactionTrailingSpacing: CGFloat = 6.0 var reactionInset: CGFloat = 0.0 - if arguments.layoutInput.displayInlineReactions, !arguments.reactions.isEmpty { - reactionInset = -1.0 + CGFloat(arguments.reactions.count) * reactionSize + CGFloat(arguments.reactions.count - 1) * reactionSpacing + reactionTrailingSpacing - - var count = 0 - for reaction in arguments.reactions { - count += Int(reaction.count) - } - - let countString: String - if count > 1000000 { - countString = "\(count / 1000000)M" - } else if count > 1000 { - countString = "\(count / 1000)K" - } else { - countString = "\(count)" - } - - if count > arguments.reactions.count { - let layoutAndApply = makeReactionCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) - reactionInset += layoutAndApply.0.size.width + 4.0 - reactionCountLayoutAndApply = layoutAndApply - } - } if arguments.replyCount > 0 { let countString: String @@ -726,6 +665,10 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { reactionInset += 12.0 } + if arguments.messageEffect != nil { + reactionInset += 13.0 + } + leftInset += reactionInset let layoutSize = CGSize(width: leftInset + impressionWidth + date.size.width + statusWidth + backgroundInsets.left + backgroundInsets.right, height: date.size.height + backgroundInsets.top + backgroundInsets.bottom) @@ -1145,59 +1088,34 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } var reactionOffset: CGFloat = leftOffset + leftInset - reactionInset + backgroundInsets.left - if arguments.layoutInput.displayInlineReactions { + + if let messageEffect = arguments.messageEffect { var validReactions = Set() - for reaction in arguments.reactions.sorted(by: { lhs, rhs in - if lhs.isSelected != rhs.isSelected { - if lhs.isSelected { - return true - } else { - return false - } - } else { - if let lhsIndex = lhs.chosenOrder, let rhsIndex = rhs.chosenOrder { - return lhsIndex < rhsIndex - } else { - return lhs.count > rhs.count - } - } - }) { + do { let node: StatusReactionNode var animateNode = true - if let current = strongSelf.reactionNodes[reaction.value] { + if let current = strongSelf.reactionNodes[.custom(messageEffect.id)] { node = current } else { animateNode = false node = StatusReactionNode() - strongSelf.reactionNodes[reaction.value] = node + strongSelf.reactionNodes[.custom(messageEffect.id)] = node } - validReactions.insert(reaction.value) + validReactions.insert(.custom(messageEffect.id)) var centerAnimation: TelegramMediaFile? - var reactionFileId: Int64? - switch reaction.value { - case .builtin: - if let availableReactions = arguments.availableReactions { - for availableReaction in availableReactions.reactions { - if availableReaction.value == reaction.value { - centerAnimation = availableReaction.centerAnimation - break - } - } - } - case let .custom(fileId): - reactionFileId = fileId - } + centerAnimation = messageEffect.staticIcon node.update( context: arguments.context, type: arguments.type, - value: reaction.value, + value: .custom(messageEffect.id), file: centerAnimation, - fileId: reactionFileId, - isSelected: reaction.isSelected, - count: Int(reaction.count), + fileId: centerAnimation?.fileId.id, + alternativeText: messageEffect.emoticon, + isSelected: false, + count: 0, theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper, animationCache: arguments.animationCache, @@ -1210,7 +1128,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } - let nodeFrame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + offset + verticalInset), size: CGSize(width: reactionSize, height: reactionSize)) + let nodeFrame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + offset + 3.0 + UIScreenPixel + verticalInset), size: CGSize(width: reactionSize, height: reactionSize)) if animateNode { animation.animator.updateFrame(layer: node.layer, frame: nodeFrame, completion: nil) } else { @@ -1259,30 +1177,6 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } } - if let (layout, apply) = reactionCountLayoutAndApply { - let node = apply() - if strongSelf.reactionCountNode !== node { - strongSelf.reactionCountNode?.removeFromSupernode() - strongSelf.addSubnode(node) - strongSelf.reactionCountNode = node - if animation.isAnimated { - node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) - } - } - let nodeFrame = CGRect(origin: CGPoint(x: reactionOffset - 4.0, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: layout.size) - animation.animator.updateFrame(layer: node.layer, frame: nodeFrame, completion: nil) - reactionOffset += 1.0 + layout.size.width + 4.0 - } else if let reactionCountNode = strongSelf.reactionCountNode { - strongSelf.reactionCountNode = nil - if animation.isAnimated { - reactionCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionCountNode] _ in - reactionCountNode?.removeFromSupernode() - }) - } else { - reactionCountNode.removeFromSupernode() - } - } - if let currentRepliesIcon = currentRepliesIcon { currentRepliesIcon.displaysAsynchronously = false if currentRepliesIcon.image !== repliesImage { @@ -1363,11 +1257,6 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } public func reactionView(value: MessageReaction.Reaction) -> UIView? { - for (id, node) in self.reactionNodes { - if id == value { - return node.iconView - } - } for (key, button) in self.reactionButtonsContainer.buttons { if key == value { return button.view.iconView @@ -1376,6 +1265,13 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { return nil } + public func messageEffectTargetView() -> UIView? { + for (_, node) in self.reactionNodes { + return node.iconView + } + return nil + } + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { for (_, button) in self.reactionButtonsContainer.buttons { if button.view.frame.contains(point) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift index c9ddf26366..4335199520 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift @@ -249,4 +249,11 @@ public class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode { } return nil } + + override public func messageEffectTargetView() -> UIView? { + if !self.interactiveFileNode.dateAndStatusNode.isHidden { + return self.interactiveFileNode.dateAndStatusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift index b4974c403a..0410d0d68c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift @@ -149,4 +149,11 @@ public final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNod } return nil } + + override public func messageEffectTargetView() -> UIView? { + if let statusNode = self.contentNode.statusNode, !statusNode.isHidden { + return statusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 2951dc6069..466041cbeb 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -528,6 +528,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -863,6 +864,13 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, return nil } + override public func messageEffectTargetView() -> UIView? { + if !self.dateAndStatusNode.isHidden { + return self.dateAndStatusNode.messageEffectTargetView() + } + return nil + } + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.dateAndStatusNode.supernode != nil, let result = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: event) { return result diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift index a010d655e0..8a1e689408 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -458,6 +458,13 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN return nil } + override public func messageEffectTargetView() -> UIView? { + if !self.interactiveVideoNode.dateAndStatusNode.isHidden { + return self.interactiveVideoNode.dateAndStatusNode.messageEffectTargetView() + } + return nil + } + override public func targetForStoryTransition(id: StoryId) -> UIView? { return self.interactiveVideoNode.targetForStoryTransition(id: id) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index b9f82bab5c..930e9591d5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -943,6 +943,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: arguments.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: arguments.message.areReactionsTags(accountPeerId: arguments.context.account.peerId), + messageEffect: arguments.message.messageEffect(availableMessageEffects: arguments.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: arguments.isPinned && !arguments.associatedData.isInPinnedListMode, hasAutoremove: arguments.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index b6ab4de49f..c7c9cdb1cd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -579,6 +579,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index ac4e260fa0..c2adbf6d9d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -874,6 +874,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr reactionPeers: dateAndStatus.dateReactionPeers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), + messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects), replyCount: dateAndStatus.dateReplies, isPinned: dateAndStatus.isPinned, hasAutoremove: message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift index b46926dc47..ab5480aea8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift @@ -145,4 +145,11 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent } return nil } + + override public func messageEffectTargetView() -> UIView? { + if let statusNode = self.contentNode.statusNode, !statusNode.isHidden { + return statusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index b5a1394c36..918afe822c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -282,6 +282,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -540,4 +541,11 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } return nil } + + override public func messageEffectTargetView() -> UIView? { + if !self.dateAndStatusNode.isHidden { + return self.dateAndStatusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift index 58d86e83ba..26c0ef9212 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift @@ -510,4 +510,11 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } return nil } + + override public func messageEffectTargetView() -> UIView? { + if !self.interactiveImageNode.dateAndStatusNode.isHidden { + return self.interactiveImageNode.dateAndStatusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index d6bfff73f4..8003318a44 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1095,6 +1095,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -1786,4 +1787,11 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } return nil } + + override public func messageEffectTargetView() -> UIView? { + if !self.statusNode.isHidden { + return self.statusNode.messageEffectTargetView() + } + return nil + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift index 5871ffeff5..2b47aa6ee7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -138,6 +138,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index ce7f21fe49..c683829648 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -631,6 +631,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index e555b4d992..b7a03af976 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -578,6 +578,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId), + messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread), hasAutoremove: item.message.isSelfExpiring, @@ -1358,6 +1359,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { return nil } + override public func messageEffectTargetView() -> UIView? { + if let statusNode = self.statusNode, !statusNode.isHidden { + return statusNode.messageEffectTargetView() + } + return nil + } + override public func getStatusNode() -> ASDisplayNode? { return self.statusNode } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index e05cc37df0..decec584c0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -749,4 +749,8 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent override public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? { return self.contentNode.reactionTargetView(value: value) } + + override public func messageEffectTargetView() -> UIView? { + return self.contentNode.messageEffectTargetView() + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index a20a910b94..bdc5470c8d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -306,7 +306,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { if let context = self?.context, let navigationController = self?.getNavigationController() { let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).startStandalone() } - }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false + }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false }, requestMessageActionCallback: { [weak self] messageId, _, _, _ in guard let self else { return diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index 66b2eb252a..ab70fa9a0d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -130,7 +130,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { return self.id } - func item(context: AccountContext, peer: Peer, controllerInteraction: ChatControllerInteraction, chatThemes: [TelegramTheme], availableReactions: AvailableReactions?) -> ListViewItem { + func item(context: AccountContext, peer: Peer, controllerInteraction: ChatControllerInteraction, chatThemes: [TelegramTheme], availableReactions: AvailableReactions?, availableMessageEffects: AvailableMessageEffects?) -> ListViewItem { switch self.entry.event.action { case let .changeTitle(_, new): var peers = SimpleDictionary() @@ -143,7 +143,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -174,14 +174,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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: availableReactions, 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) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -212,7 +212,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -231,7 +231,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: availableReactions, 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) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -262,7 +262,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -300,7 +300,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: availableReactions, 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) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -319,7 +319,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -346,7 +346,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -373,7 +373,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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: @@ -404,7 +404,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -428,7 +428,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: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: message.threadId, 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: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -450,7 +450,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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): @@ -495,7 +495,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -518,7 +518,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: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: message.threadId, 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: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, 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) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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 { @@ -579,7 +579,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -628,7 +628,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } 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: message.threadId, 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: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: additionalContent) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: additionalContent) } case .participantJoin, .participantLeave: var peers = SimpleDictionary() @@ -646,7 +646,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -663,7 +663,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -794,7 +794,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -1034,7 +1034,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1063,7 +1063,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1093,7 +1093,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -1152,7 +1152,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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: @@ -1180,7 +1180,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -1203,7 +1203,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: 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: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, 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) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -1259,7 +1259,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1281,12 +1281,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -1317,7 +1317,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1354,7 +1354,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1388,7 +1388,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1417,7 +1417,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1448,7 +1448,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1474,7 +1474,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1500,7 +1500,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1526,7 +1526,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1557,7 +1557,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1588,7 +1588,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1659,7 +1659,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1690,7 +1690,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1730,7 +1730,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1757,7 +1757,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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: @@ -1782,7 +1782,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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] = [] @@ -1805,7 +1805,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: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: message.threadId, 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: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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() @@ -1825,7 +1825,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1846,7 +1846,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1919,7 +1919,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1957,7 +1957,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1978,7 +1978,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -1999,7 +1999,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -2068,7 +2068,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -2147,7 +2147,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -2182,7 +2182,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, 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? @@ -2210,7 +2210,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: availableReactions, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil, chatThemes: chatThemes), 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil, chatThemes: chatThemes), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeEmojiPack(_, new): var peers = SimpleDictionary() var author: Peer? @@ -2239,7 +2239,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: availableReactions, savedMessageTags: 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } } @@ -2321,8 +2321,8 @@ func chatRecentActionsHistoryPreparedTransition(from fromEntries: [ChatRecentAct let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdatesReversed(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, peer: peer, controllerInteraction: controllerInteraction, chatThemes: chatThemes, availableReactions: availableReactions), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, peer: peer, controllerInteraction: controllerInteraction, chatThemes: chatThemes, availableReactions: availableReactions), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, peer: peer, controllerInteraction: controllerInteraction, chatThemes: chatThemes, availableReactions: availableReactions, availableMessageEffects: nil), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, peer: peer, controllerInteraction: controllerInteraction, chatThemes: chatThemes, availableReactions: availableReactions, availableMessageEffects: nil), directionHint: nil) } return ChatRecentActionsHistoryTransition(filteredEntries: toEntries, type: type, deletions: deletions, insertions: insertions, updates: updates, canLoadEarlier: canLoadEarlier, displayingResults: displayingResults, searchResultsState: searchResultsState, synchronous: !toggledDeletedMessageIds.isEmpty, isEmpty: toEntries.isEmpty) } diff --git a/submodules/TelegramUI/Components/Chat/TopMessageReactions/Sources/TopMessageReactions.swift b/submodules/TelegramUI/Components/Chat/TopMessageReactions/Sources/TopMessageReactions.swift index 5ab5f01380..080aeb97a6 100644 --- a/submodules/TelegramUI/Components/Chat/TopMessageReactions/Sources/TopMessageReactions.swift +++ b/submodules/TelegramUI/Components/Chat/TopMessageReactions/Sources/TopMessageReactions.swift @@ -421,40 +421,33 @@ public func topMessageReactions(context: AccountContext, message: Message, subPe } public func effectMessageReactions(context: AccountContext) -> Signal<[ReactionItem], NoError> { - return context.engine.stickers.availableReactions() + return context.engine.stickers.availableMessageEffects() |> take(1) - |> map { availableReactions -> [ReactionItem] in - guard let availableReactions else { + |> map { availableMessageEffects -> [ReactionItem] in + guard let availableMessageEffects else { return [] } var result: [ReactionItem] = [] - var existingIds = Set() + var existingIds = Set() - for reaction in availableReactions.reactions { - guard let centerAnimation = reaction.centerAnimation else { + for messageEffect in availableMessageEffects.messageEffects { + if existingIds.contains(messageEffect.id) { continue } - guard let aroundAnimation = reaction.aroundAnimation else { - continue - } - if !reaction.isEnabled { - continue - } - if existingIds.contains(reaction.value) { - continue - } - existingIds.insert(reaction.value) + existingIds.insert(messageEffect.id) + + let mainFile: TelegramMediaFile = messageEffect.effectSticker result.append(ReactionItem( - reaction: ReactionItem.Reaction(rawValue: reaction.value), - appearAnimation: reaction.appearAnimation, - stillAnimation: reaction.selectAnimation, - listAnimation: centerAnimation, - largeListAnimation: reaction.activateAnimation, - applicationAnimation: aroundAnimation, - largeApplicationAnimation: reaction.effectAnimation, - isCustom: false + reaction: ReactionItem.Reaction(rawValue: .custom(messageEffect.id)), + appearAnimation: mainFile, + stillAnimation: mainFile, + listAnimation: mainFile, + largeListAnimation: mainFile, + applicationAnimation: nil, + largeApplicationAnimation: nil, + isCustom: true )) } diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 8d2452d1dc..32efa41346 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -115,6 +115,14 @@ public struct OpenMessageParams { } } +public final class ChatSendMessageEffect { + public let id: Int64 + + public init(id: Int64) { + self.id = id + } +} + public final class ChatControllerInteraction: ChatControllerInteractionProtocol { public enum OpenPeerSource { case `default` @@ -154,7 +162,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol public let tapMessage: ((Message) -> Void)? public let clickThroughMessage: () -> Void public let toggleMessagesSelection: ([MessageId], Bool) -> Void - public let sendCurrentMessage: (Bool) -> Void + public let sendCurrentMessage: (Bool, ChatSendMessageEffect?) -> Void public let sendMessage: (String) -> Void public let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool public let sendEmoji: (String, ChatTextInputTextCustomEmojiAttribute, Bool) -> Void @@ -279,7 +287,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, - sendCurrentMessage: @escaping (Bool) -> Void, + sendCurrentMessage: @escaping (Bool, ChatSendMessageEffect?) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?, [ItemCollectionId]) -> Bool, sendEmoji: @escaping (String, ChatTextInputTextCustomEmojiAttribute, Bool) -> Void, diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift index 33bf1a054c..329943a079 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift @@ -2163,4 +2163,140 @@ public extension EmojiPagerContentComponent { ) } } + + static func messageEffectsInputData( + context: AccountContext, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + hasSearch: Bool, + hideBackground: Bool = false + ) -> Signal { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let isPremiumDisabled = premiumConfiguration.isPremiumDisabled + + let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings + + let searchCategories: Signal = .single(nil) + + return combineLatest( + hasPremium(context: context, chatPeerId: nil, premiumIfSavedMessages: false), + context.engine.stickers.availableMessageEffects(), + searchCategories + ) + |> map { hasPremium, availableMessageEffects, searchCategories -> EmojiPagerContentComponent in + struct ItemGroup { + var supergroupId: AnyHashable + var id: AnyHashable + var title: String? + var subtitle: String? + var actionButtonTitle: String? + var isPremiumLocked: Bool + var isFeatured: Bool + var displayPremiumBadges: Bool + var hasEdit: Bool + var headerItem: EntityKeyboardAnimationData? + var items: [EmojiPagerContentComponent.Item] + } + var itemGroups: [ItemGroup] = [] + var itemGroupIndexById: [AnyHashable: Int] = [:] + + if let availableMessageEffects { + var reactionEffects: [AvailableMessageEffects.MessageEffect] = [] + var stickerEffects: [AvailableMessageEffects.MessageEffect] = [] + for messageEffect in availableMessageEffects.messageEffects { + if messageEffect.effectAnimation != nil { + reactionEffects.append(messageEffect) + } else { + stickerEffects.append(messageEffect) + } + } + + for i in 0 ..< 2 { + let groupId = i == 0 ? "reactions" : "stickers" + for item in i == 0 ? reactionEffects : stickerEffects { + if item.isPremium && isPremiumDisabled { + continue + } + + let itemFile: TelegramMediaFile = item.effectSticker + + var tintMode: Item.TintMode = .none + if itemFile.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: itemFile, partialReference: .none) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: itemFile, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + //TODO:localize + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: i == 0 ? nil : "Message Effects", subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, hasEdit: false, headerItem: nil, items: [resultItem])) + } + } + } + } + + let warpContentsOnEdges: Bool = true + + let allItemGroups = itemGroups.map { group -> EmojiPagerContentComponent.ItemGroup in + let hasClear = false + let isEmbedded = false + + return EmojiPagerContentComponent.ItemGroup( + supergroupId: group.supergroupId, + groupId: group.id, + title: group.title, + subtitle: group.subtitle, + badge: nil, + actionButtonTitle: group.actionButtonTitle, + isFeatured: group.isFeatured, + isPremiumLocked: group.isPremiumLocked, + isEmbedded: isEmbedded, + hasClear: hasClear, + hasEdit: group.hasEdit, + collapsedLineCount: nil, + displayPremiumBadges: group.displayPremiumBadges, + headerItem: group.headerItem, + fillWithLoadingPlaceholders: false, + items: group.items + ) + } + + return EmojiPagerContentComponent( + id: "stickers", + context: context, + avatarPeer: nil, + animationCache: animationCache, + animationRenderer: animationRenderer, + inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder(), + panelItemGroups: [], + contentItemGroups: allItemGroups, + itemLayoutType: .detailed, + itemContentUniqueId: nil, + searchState: .empty(hasResults: false), + warpContentsOnEdges: warpContentsOnEdges, + hideBackground: hideBackground, + displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, + searchCategories: searchCategories, + searchInitiallyHidden: true, + searchAlwaysActive: false, + searchIsPlaceholderOnly: false, + searchUnicodeEmojiOnly: false, + emptySearchResults: nil, + enableLongPress: false, + selectedItems: Set(), + customTintColor: nil + ) + } + } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 9df94346e4..9dd6384b9d 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -6562,7 +6562,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate contextItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) contextItems.append(.separator) @@ -6623,7 +6623,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate tipSignal: nil, dismissed: nil ) - c.pushItems(items: .single(items)) + c?.pushItems(items: .single(items)) }))) case .editing: menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_ReplaceSticker, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index 4383cb8e8c..0d013cd5c5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -396,7 +396,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionPromote, icon: { _ in return nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { action(member, .promote) }) }))) @@ -406,7 +406,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionRestrict, icon: { _ in return nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { action(member, .restrict) }) }))) @@ -414,7 +414,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Delete, textColor: .destructive, icon: { _ in return nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { action(member, .remove) }) }))) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 3843810488..5b6cc6e9ba 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -2843,7 +2843,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController { if let channel = currentPeer as? TelegramChannel, channel.flags.contains(.isForum), let threadId = message.threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone() @@ -2885,7 +2885,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if let linkForCopying = linkForCopying { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuCopyLink, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss(completion: {}) + c?.dismiss(completion: {}) UIPasteboard.general.string = linkForCopying let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -2897,7 +2897,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else if message.id.peerId.namespace != Namespaces.Peer.SecretChat && message.minAutoremoveOrClearTimeout == nil { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.forwardMessages(messageIds: Set([message.id])) } @@ -2909,7 +2909,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let presentationData = strongSelf.presentationData let peerId = strongSelf.peerId items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, _ in - c.setItems(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)) + c?.setItems(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)) |> map { peer -> ContextController.Items in var items: [ContextMenuItem] = [] let messageIds = [message.id] @@ -2933,7 +2933,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro globalTitle = presentationData.strings.Conversation_DeleteMessagesForEveryone } items.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() @@ -2952,7 +2952,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } items.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).startStandalone() @@ -2970,7 +2970,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.separator) items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true) strongSelf.expandTabs(animated: true) @@ -3005,7 +3005,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController { if let channel = currentPeer as? TelegramChannel, channel.flags.contains(.isForum), let threadId = message.threadId { let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone() @@ -3049,7 +3049,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else if message.id.peerId.namespace != Namespaces.Peer.SecretChat { items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.forwardMessages(messageIds: [message.id]) } @@ -3059,7 +3059,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if actions.options.contains(.deleteLocally) || actions.options.contains(.deleteGlobally) { items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in - c.setItems(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)) + c?.setItems(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)) |> map { peer -> ContextController.Items in var items: [ContextMenuItem] = [] let messageIds = [message.id] @@ -3083,7 +3083,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone } items.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() @@ -3102,7 +3102,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } items.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { c, f in - c.dismiss(completion: { + c?.dismiss(completion: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).startStandalone() @@ -3167,7 +3167,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring), additive: false) } strongSelf.paneContainerNode.updateSelectedMessageIds(strongSelf.state.selectedMessageIds, animated: true) - }, sendCurrentMessage: { _ in + }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false @@ -5406,7 +5406,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -5437,7 +5437,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.openCustomMute() }))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) items.append(.separator) @@ -5949,7 +5949,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -6003,9 +6003,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.controller?.view.endEditing(true) }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) } @@ -6014,7 +6014,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) + } }))) } @@ -6149,7 +6151,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -6210,9 +6212,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.controller?.view.endEditing(true) }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) } @@ -6221,7 +6223,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: channel) + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: channel) + } }))) } @@ -6284,7 +6288,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -6338,9 +6342,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.controller?.view.endEditing(true) }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) } @@ -6370,7 +6374,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: group) + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: group) + } }))) } @@ -6646,7 +6652,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -6664,7 +6670,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro subItems.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil - }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) let beginClear: (InteractiveHistoryClearingType) -> Void = { [weak self] type in guard let strongSelf = self else { @@ -6953,7 +6959,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isMyProfile { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -6963,7 +6969,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7029,7 +7035,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isMyProfile { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BioActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7056,7 +7062,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro copyText = self.presentationData.strings.ChannelProfile_AboutActionCopy } items.append(.action(ContextMenuActionItem(text: copyText, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7064,7 +7070,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let (canTranslate, language) = canTranslateText(context: self.context, text: bioText, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) if canTranslate { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7119,7 +7125,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isMyProfile { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7130,7 +7136,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7152,14 +7158,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro action: noAction ))) subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } let _ = self.context.engine.accountData.updateAccountBusinessHours(businessHours: nil).startStandalone() } }))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) } @@ -7199,7 +7205,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if businessLocation.coordinates != nil { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionOpen, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Editor/LocationSmall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -7210,7 +7216,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if !businessLocation.address.isEmpty { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7218,7 +7224,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isMyProfile { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7242,14 +7248,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro action: noAction ))) subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } let _ = self.context.engine.accountData.updateAccountBusinessLocation(businessLocation: nil).startStandalone() } }))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) } @@ -7292,7 +7298,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isMyProfile { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7304,7 +7310,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7391,7 +7397,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if strongSelf.isMyProfile { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss { + c?.dismiss { guard let self else { return } @@ -7403,12 +7409,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formattedPhoneNumber == formatPhoneNumber(context: strongSelf.context, number: peerPhoneNumber) { if !strongSelf.isMyProfile { items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { telegramCallAction(false) } }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { telegramCallAction(true) } }))) @@ -7416,7 +7422,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if !formattedPhoneNumber.hasPrefix("+888") { if !strongSelf.isMyProfile { items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { phoneCallAction() } }))) @@ -7425,7 +7431,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro isAnonymousNumber = true } items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } }))) @@ -7434,7 +7440,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if !strongSelf.isMyProfile { items.append( .action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { phoneCallAction() } })) @@ -7445,7 +7451,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } items.append( .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c.dismiss { + c?.dismiss { copyAction() } })) @@ -8002,7 +8008,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, iconPosition: .left, action: { (c, _) in - if let backAction = backAction { + if let c, let backAction = backAction { backAction(c) } }))) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index a0f7854d50..b3edf843be 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -1859,7 +1859,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } /*items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Edit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -1872,7 +1872,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr if !item.isForwardingDisabled, case .everyone = item.privacy?.base { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Forward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -1914,7 +1914,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr if canManage { items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } diff --git a/submodules/TelegramUI/Components/PeerReportScreen/Sources/PeerReportScreen.swift b/submodules/TelegramUI/Components/PeerReportScreen/Sources/PeerReportScreen.swift index 1e9b7fbbb4..d1a4f97e04 100644 --- a/submodules/TelegramUI/Components/PeerReportScreen/Sources/PeerReportScreen.swift +++ b/submodules/TelegramUI/Components/PeerReportScreen/Sources/PeerReportScreen.swift @@ -57,7 +57,7 @@ public func presentPeerReportOptions( items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, iconPosition: .left, action: { (c, _) in - c.popItems() + c?.popItems() }))) items.append(.separator) } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index c293262100..8783e49359 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -691,8 +691,8 @@ final class PeerSelectionControllerNode: ASDisplayNode { hasEntityKeyboard = true } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputNode.textView, canSendWhenOnline: false, completion: { - }, sendMessage: { [weak textInputPanelNode] mode in + let controller = makeChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputNode.textView, emojiViewProvider: textInputPanelNode.emojiViewProvider, canSendWhenOnline: false, completion: { + }, sendMessage: { [weak textInputPanelNode] mode, _ in switch mode { case .generic: textInputPanelNode?.sendMessage(.generic) @@ -701,10 +701,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { case .whenOnline: textInputPanelNode?.sendMessage(.whenOnline) } - }, schedule: { [weak textInputPanelNode] in + }, schedule: { [weak textInputPanelNode] _ in textInputPanelNode?.sendMessage(.schedule) }) - controller.emojiViewProvider = textInputPanelNode.emojiViewProvider strongSelf.presentInGlobalOverlay(controller, nil) }, openScheduledMessages: { }, openPeersNearby: { diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift index fc531baa09..83f55acc75 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift @@ -550,7 +550,7 @@ final class BusinessLinksSetupScreenComponent: Component { textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } @@ -563,7 +563,7 @@ final class BusinessLinksSetupScreenComponent: Component { textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let self else { return } diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index d8dc330f2c..ae05a9c642 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2008,7 +2008,7 @@ final class StorageUsageScreenComponent: Component { text: presentationData.strings.StorageManagement_PeerShowDetails, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self else { return } @@ -2026,7 +2026,7 @@ final class StorageUsageScreenComponent: Component { } }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self, let component = self.component, let controller = self.controller?() else { return } @@ -2049,7 +2049,7 @@ final class StorageUsageScreenComponent: Component { text: presentationData.strings.StorageManagement_ContextSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { }) guard let self, let aggregatedData = self.aggregatedData else { @@ -2544,7 +2544,7 @@ final class StorageUsageScreenComponent: Component { text: openTitle, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Expand"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self else { return } @@ -2554,7 +2554,7 @@ final class StorageUsageScreenComponent: Component { )) items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self, let component = self.component, let controller = self.controller?(), let navigationController = controller.navigationController as? NavigationController else { return } @@ -2580,7 +2580,7 @@ final class StorageUsageScreenComponent: Component { items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuSelect, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { }) guard let self, let aggregatedData = self.aggregatedData else { @@ -2641,7 +2641,7 @@ final class StorageUsageScreenComponent: Component { text: openTitle, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Expand"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self else { return } @@ -2657,7 +2657,7 @@ final class StorageUsageScreenComponent: Component { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self, let component = self.component, let controller = self.controller?(), let navigationController = controller.navigationController as? NavigationController else { return } @@ -2684,7 +2684,7 @@ final class StorageUsageScreenComponent: Component { text: aggregatedData.selectionState.selectedMessages.contains(messageId) ? presentationData.strings.StorageManagement_ContextDeselect : presentationData.strings.StorageManagement_ContextSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { }) guard let self, let aggregatedData = self.aggregatedData else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index a0219c6ba5..26e97b679b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -6169,7 +6169,7 @@ public final class StoryItemSetContainerComponent: Component { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) items.append(.custom(SliderContextItem(minValue: 0.2, maxValue: 2.5, value: baseRate, valueChanged: { [weak self] newValue, done in @@ -6260,11 +6260,11 @@ public final class StoryItemSetContainerComponent: Component { return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in guard let self else { - c.dismiss(completion: nil) + c?.dismiss(completion: nil) return } - c.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) + c?.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) }))) items.append(.separator) } @@ -6476,11 +6476,11 @@ public final class StoryItemSetContainerComponent: Component { return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in guard let self else { - c.dismiss(completion: nil) + c?.dismiss(completion: nil) return } - c.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) + c?.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) }))) items.append(.separator) } @@ -6791,11 +6791,11 @@ public final class StoryItemSetContainerComponent: Component { return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in guard let self else { - c.dismiss(completion: nil) + c?.dismiss(completion: nil) return } - c.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) + c?.pushItems(items: self.contextMenuSpeedItems(value: baseRatePromise) |> map { ContextController.Items(content: .list($0)) }) }))) items.append(.separator) } diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 9e10492a54..4677d5071c 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -241,6 +241,19 @@ public final class AccountContextImpl: AccountContext { return availableReactionsValue.get() } + private var availableMessageEffectsValue: Promise? + public var availableMessageEffects: Signal { + let availableMessageEffectsValue: Promise + if let current = self.availableMessageEffectsValue { + availableMessageEffectsValue = current + } else { + availableMessageEffectsValue = Promise() + self.availableMessageEffectsValue = availableMessageEffectsValue + availableMessageEffectsValue.set(self.engine.stickers.availableMessageEffects()) + } + return availableMessageEffectsValue.get() + } + private var userLimitsConfigurationDisposable: Disposable? public private(set) var userLimits: EngineConfiguration.UserLimits diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 35ae3b57a3..6d982168ea 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -3055,13 +3055,13 @@ extension ChatControllerImpl { } else { var contextItems: [ContextMenuItem] = [] contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessagesFor(EnginePeer(peer).compactDisplayTitle).string, textColor: .primary, icon: { _ in nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { pinAction(true, false) }) }))) contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessagesForMe, textColor: .primary, icon: { _ in nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { pinAction(true, true) }) }))) @@ -3074,13 +3074,13 @@ extension ChatControllerImpl { var contextItems: [ContextMenuItem] = [] contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessageAlert_PinAndNotifyMembers, textColor: .primary, icon: { _ in nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { pinAction(true, false) }) }))) contextItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, textColor: .primary, icon: { _ in nil }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { pinAction(false, false) }) }))) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift index 611e1b9896..3fa9e43879 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift @@ -476,7 +476,7 @@ extension ChatControllerImpl { self.updateDownButtonVisibility() } - func sendMediaRecording(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, viewOnce: Bool = false) { + func sendMediaRecording(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, viewOnce: Bool = false, messageEffect: ChatSendMessageEffect? = nil) { self.chatDisplayNode.updateRecordedMediaDeleted(false) guard let recordedMediaPreview = self.presentationInterfaceState.interfaceState.mediaDraftState else { diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 469ed2498e..179f566e64 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -354,7 +354,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch subItems.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -379,7 +379,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch f(.default) }))) - c.pushItems(items: .single(ContextController.Items(content: .list(subItems), dismissed: { [weak contentNode] in + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems), dismissed: { [weak contentNode] in guard let contentNode else { return } diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift index 5084606da5..82db6f7d73 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift @@ -11,6 +11,13 @@ import ChatSendMessageActionUI import AccountContext import TopMessageReactions import ReactionSelectionNode +import ChatControllerInteraction + +extension ChatSendMessageEffect { + convenience init(_ effect: ChatSendMessageActionSheetController.MessageEffect) { + self.init(id: effect.id) + } +} func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, node: ASDisplayNode, gesture: ContextGesture) { guard let peerId = selfController.chatLocation.peerId, let textInputView = selfController.chatDisplayNode.textInputView(), let layout = selfController.validLayout else { @@ -61,22 +68,22 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: selfController.context.sharedContext.accountManager, count: 4).startStandalone() } - let controller = ChatSendMessageActionSheetController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, peerId: selfController.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputView, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak selfController] in + let controller = makeChatSendMessageActionSheetController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, peerId: selfController.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputView, emojiViewProvider: selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider, wallpaperBackgroundNode: selfController.chatDisplayNode.backgroundNode, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak selfController] in guard let selfController else { return } selfController.supportedOrientations = previousSupportedOrientations - }, sendMessage: { [weak selfController] mode in + }, sendMessage: { [weak selfController] mode, messageEffect in guard let selfController else { return } switch mode { case .generic: - selfController.controllerInteraction?.sendCurrentMessage(false) + selfController.controllerInteraction?.sendCurrentMessage(false, messageEffect.flatMap(ChatSendMessageEffect.init)) case .silently: - selfController.controllerInteraction?.sendCurrentMessage(true) + selfController.controllerInteraction?.sendCurrentMessage(true, messageEffect.flatMap(ChatSendMessageEffect.init)) case .whenOnline: - selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp) { [weak selfController] in + selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp, messageEffect: messageEffect.flatMap(ChatSendMessageEffect.init)) { [weak selfController] in guard let selfController else { return } @@ -86,13 +93,12 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no selfController.openScheduledMessages() } } - }, schedule: { [weak selfController] in + }, schedule: { [weak selfController] messageEffect in guard let selfController else { return } selfController.controllerInteraction?.scheduleCurrentMessage() }, reactionItems: effectItems) - controller.emojiViewProvider = selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider selfController.sendMessageActionsController = controller if layout.isNonExclusive { selfController.present(controller, in: .window(.root)) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 866f086247..99d13f1d54 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1731,12 +1731,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: text as NSString) }) } - }, sendCurrentMessage: { [weak self] silentPosting in + }, sendCurrentMessage: { [weak self] silentPosting, messageEffect in if let strongSelf = self { if let _ = strongSelf.presentationInterfaceState.interfaceState.mediaDraftState { - strongSelf.sendMediaRecording(silentPosting: silentPosting) + strongSelf.sendMediaRecording(silentPosting: silentPosting, messageEffect: messageEffect) } else { - strongSelf.chatDisplayNode.sendCurrentMessage(silentPosting: silentPosting) + strongSelf.chatDisplayNode.sendCurrentMessage(silentPosting: silentPosting, messageEffect: messageEffect) } } }, sendMessage: { [weak self] text in @@ -3306,10 +3306,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.presentScheduleTimePicker(completion: { [weak self] time in if let strongSelf = self { if let _ = strongSelf.presentationInterfaceState.interfaceState.mediaDraftState { - strongSelf.sendMediaRecording(scheduleTime: time) + strongSelf.sendMediaRecording(scheduleTime: time, messageEffect: nil) } else { let silentPosting = strongSelf.presentationInterfaceState.interfaceState.silentPosting - strongSelf.chatDisplayNode.sendCurrentMessage(silentPosting: silentPosting, scheduleTime: time) { [weak self] in + strongSelf.chatDisplayNode.sendCurrentMessage(silentPosting: silentPosting, scheduleTime: time, messageEffect: nil) { [weak self] in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: strongSelf.presentationInterfaceState.subject != .scheduledMessages, { $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } diff --git a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift index 0f361cc0ec..642feb20c2 100644 --- a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift +++ b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift @@ -488,7 +488,7 @@ extension ChatControllerImpl { f(.dismissWithoutContent) commit() } else { - c.dismiss(completion: { + c?.dismiss(completion: { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { commit() }) @@ -541,7 +541,7 @@ extension ChatControllerImpl { f(.dismissWithoutContent) commit() } else { - c.dismiss(completion: { + c?.dismiss(completion: { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { commit() }) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 2e36be9e48..ed4d9de045 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3821,7 +3821,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } - func sendCurrentMessage(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, completion: @escaping () -> Void = {}) { + func sendCurrentMessage(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, messageEffect: ChatSendMessageEffect? = nil, completion: @escaping () -> Void = {}) { if let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { self.historyNode.justSentTextMessage = true @@ -4031,6 +4031,14 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } + if !messages.isEmpty, let messageEffect { + messages[0] = messages[0].withUpdatedAttributes { attributes in + var attributes = attributes + attributes.append(EffectMessageAttribute(id: messageEffect.id)) + return attributes + } + } + if !messages.isEmpty || postEmptyMessages || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil { if let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds { var attributes: [MessageAttribute] = [] diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index ce7728a103..e36db96f85 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -85,7 +85,7 @@ extension ChatControllerImpl { a(.default) return } - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self else { return } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 3b768daac0..84eb599a98 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -334,6 +334,7 @@ private func extractAssociatedData( currentlyPlayingMessageId: MessageIndex?, isCopyProtectionEnabled: Bool, availableReactions: AvailableReactions?, + availableMessageEffects: AvailableMessageEffects?, savedMessageTags: SavedMessageTags?, defaultReaction: MessageReaction.Reaction?, isPremium: Bool, @@ -402,7 +403,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, 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, isInline: isInline) + 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, availableMessageEffects: availableMessageEffects, 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, isInline: isInline) } private extension ChatHistoryLocationInput { @@ -1459,6 +1460,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } let availableReactions: Signal = (context as! AccountContextImpl).availableReactions + let availableMessageEffects: Signal = (context as! AccountContextImpl).availableMessageEffects + let savedMessageTags: Signal if chatLocation.peerId == self.context.account.peerId { savedMessageTags = context.engine.stickers.savedMessageTagData() @@ -1570,6 +1573,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, + availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, @@ -1582,7 +1586,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, savedMessageTags, 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, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers in let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises func applyHole() { @@ -1801,7 +1805,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, 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, isInline: !rotated) + let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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, isInline: !rotated) var includeEmbeddedSavedChatInfo = false if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated { @@ -3528,12 +3532,18 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if let currentRange = strongSelf.controllerInteraction.unreadMessageRange[rangeKey] { if currentRange.upperBound < (updatedIncomingId + 1) { - strongSelf.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: peerId, namespace: namespace)] = currentRange.lowerBound ..< (updatedIncomingId + 1) - unreadMessageRangeUpdated = true + let updatedRange = currentRange.lowerBound ..< (updatedIncomingId + 1) + if strongSelf.controllerInteraction.unreadMessageRange[rangeKey] != updatedRange { + strongSelf.controllerInteraction.unreadMessageRange[rangeKey] = updatedRange + unreadMessageRangeUpdated = true + } } } else { - strongSelf.controllerInteraction.unreadMessageRange[rangeKey] = (previousIncomingId + 1) ..< (updatedIncomingId + 1) - unreadMessageRangeUpdated = true + let updatedRange = (previousIncomingId + 1) ..< (updatedIncomingId + 1) + if strongSelf.controllerInteraction.unreadMessageRange[rangeKey] != updatedRange { + strongSelf.controllerInteraction.unreadMessageRange[rangeKey] = updatedRange + unreadMessageRangeUpdated = true + } } } case .indexBased: @@ -3547,6 +3557,33 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto //print("Read from \(previousPeerReadState) up to \(updatedPeerReadState)") } } + } else if case let .peer(peerId) = strongSelf.chatLocation, case let .peer(updatedReadStates) = transition.historyView.originalView.transientReadStates { + if let updatedPeerReadState = updatedReadStates[peerId] { + for (namespace, updatedState) in updatedPeerReadState.states { + switch updatedState { + case let .idBased(updatedIncomingId, _, _, _, _): + let rangeKey = UnreadMessageRangeKey(peerId: peerId, namespace: namespace) + + if let currentRange = strongSelf.controllerInteraction.unreadMessageRange[rangeKey] { + if currentRange.upperBound < (updatedIncomingId + 1) { + let updatedRange = currentRange.lowerBound ..< (updatedIncomingId + 1) + if strongSelf.controllerInteraction.unreadMessageRange[rangeKey] != updatedRange { + strongSelf.controllerInteraction.unreadMessageRange[rangeKey] = updatedRange + unreadMessageRangeUpdated = true + } + } + } else { + let updatedRange = (updatedIncomingId + 1) ..< (Int32.max - 1) + if strongSelf.controllerInteraction.unreadMessageRange[rangeKey] != updatedRange { + strongSelf.controllerInteraction.unreadMessageRange[rangeKey] = updatedRange + unreadMessageRangeUpdated = true + } + } + case .indexBased: + break + } + } + } } strongSelf.historyView = transition.historyView diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 2b8c603fc7..fa7fc6eecb 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -494,7 +494,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState subItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, textColor: .primary, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, iconPosition: .left, action: { c, _ in - c.popItems() + c?.popItems() }))) subItems.append(.separator) @@ -503,7 +503,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState subItems.append(.action(ContextMenuActionItem(text: sponsorInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return nil }, iconSource: nil, action: { [weak controllerInteraction] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { UIPasteboard.general.string = sponsorInfo let content: UndoOverlayContent = .copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied) @@ -515,7 +515,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState subItems.append(.action(ContextMenuActionItem(text: additionalInfo, textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return nil }, iconSource: nil, action: { [weak controllerInteraction] c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { UIPasteboard.general.string = additionalInfo let content: UndoOverlayContent = .copy(text: presentationData.strings.Chat_ContextMenu_AdSponsorInfoCopied) @@ -524,7 +524,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }))) } - c.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) }))) actions.append(.separator) } @@ -572,7 +572,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { controllerInteraction.openNoAdsDemo() }) }))) @@ -589,7 +589,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { var replaceImpl: ((ViewController) -> Void)? let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) @@ -1112,7 +1112,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in interfaceInteraction.setupReplyMessage(messages[0].id, { transition, completed in - c.dismiss(result: .custom(transition), completion: { + c?.dismiss(result: .custom(transition), completion: { completed() }) }) @@ -1401,7 +1401,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: text, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replies"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { controllerInteraction.openMessageReplies(messages[0].id, true, true) }) }))) @@ -1687,7 +1687,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { controllerInteraction.openMessageStats(messages[0].id) }) }))) @@ -1700,7 +1700,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ViewInChannel, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in - c.dismiss(completion: { + c?.dismiss(completion: { guard let navigationController = controllerInteraction.navigationController() else { return } diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index acbcf0c576..3b9af15fca 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -680,7 +680,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, Chat return } - c.dismiss(completion: { [weak self] in + c?.dismiss(completion: { [weak self] in guard let self, let item = self.items.first(where: { $0.reaction == reaction }) else { return } diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index 5ca9802b74..32ca974cf6 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -298,7 +298,7 @@ final class ChatTranslationPanelNode: ASDisplayNode { } } - c.pushItems(items: .single(ContextController.Items( + c?.pushItems(items: .single(ContextController.Items( content: .custom( TranslationLanguagesContextMenuContent( context: self.context, @@ -322,7 +322,7 @@ final class ChatTranslationPanelNode: ASDisplayNode { items.append(.action(ContextMenuActionItem(text: doNotTranslateTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) self?.interfaceInteraction?.addDoNotTranslateLanguage(translationState.fromLang) }))) @@ -330,7 +330,7 @@ final class ChatTranslationPanelNode: ASDisplayNode { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_Translation_Hide, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) self?.interfaceInteraction?.hideTranslationPanel() }))) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index b6382e7396..25c5a57bbc 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -86,7 +86,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu }, navigateToThreadMessage: { _, _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in - }, sendCurrentMessage: { _ in + }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index a5da8cbfb9..4b198a1182 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1705,7 +1705,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { tapMessage?(message) }, clickThroughMessage: { clickThroughMessage?() - }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in + }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in }, openUrl: { _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in @@ -1791,7 +1791,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, savedMessageTags: nil, 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, availableMessageEffects: nil, 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 { diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 99f7139cc7..51b3bcf008 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1912,7 +1912,7 @@ public final class WebAppController: ViewController, AttachmentContainable { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_Settings, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Settings"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) if let strongSelf = self { strongSelf.controllerNode.sendSettingsButtonEvent() @@ -1924,7 +1924,7 @@ public final class WebAppController: ViewController, AttachmentContainable { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_OpenBot, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) guard let strongSelf = self else { return @@ -1948,7 +1948,7 @@ public final class WebAppController: ViewController, AttachmentContainable { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_ReloadPage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reload"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) self?.controllerNode.webView?.reload() }))) @@ -1956,7 +1956,7 @@ public final class WebAppController: ViewController, AttachmentContainable { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_TermsOfUse, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) guard let self, let navigationController = self.getNavigationController() else { return @@ -1976,7 +1976,7 @@ public final class WebAppController: ViewController, AttachmentContainable { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_RemoveBot, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in - c.dismiss(completion: nil) + c?.dismiss(completion: nil) if let strongSelf = self { let presentationData = context.sharedContext.currentPresentationData.with { $0 }