From fb517e49c947c473e1290a7472b079e6548d23d6 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 5 Jan 2024 10:55:59 +0400 Subject: [PATCH 1/6] [WIP] Privacy update --- .../Sources/AccountContext.swift | 6 +- .../Sources/ChatListSearchListPaneNode.swift | 6 +- .../Sources/PeerPresenceStatusManager.swift | 2 +- submodules/SearchBarNode/BUILD | 1 + .../SearchBarNode/Sources/SearchBarNode.swift | 50 +- submodules/TelegramApi/Sources/Api0.swift | 12 + submodules/TelegramApi/Sources/Api15.swift | 36 + submodules/TelegramApi/Sources/Api19.swift | 106 +- submodules/TelegramApi/Sources/Api20.swift | 56 + submodules/TelegramApi/Sources/Api22.swift | 12 + submodules/TelegramApi/Sources/Api23.swift | 216 +- submodules/TelegramApi/Sources/Api24.swift | 354 +-- submodules/TelegramApi/Sources/Api25.swift | 346 ++- submodules/TelegramApi/Sources/Api26.swift | 342 +-- submodules/TelegramApi/Sources/Api27.swift | 366 ++-- submodules/TelegramApi/Sources/Api28.swift | 450 ++-- submodules/TelegramApi/Sources/Api29.swift | 652 +++--- submodules/TelegramApi/Sources/Api30.swift | 672 +++--- submodules/TelegramApi/Sources/Api31.swift | 308 +++ submodules/TelegramApi/Sources/Api32.swift | 74 +- .../ApiUtils/ReactionsMessageAttribute.swift | 7 +- .../ApiUtils/TelegramUserPresence.swift | 26 +- .../Sources/State/AccountTaskManager.swift | 2 + .../State/HistoryViewStateValidation.swift | 4 +- .../TelegramCore/Sources/State/Holes.swift | 4 +- .../State/ManagedLocalInputActivities.swift | 2 +- .../Sources/State/ManagedRecentStickers.swift | 51 + .../Sources/State/SavedMessageTags.swift | 196 ++ .../Sources/State/Serialization.swift | 2 +- .../SyncCore/SyncCore_Namespaces.swift | 2 + .../SyncCore_ReactionsMessageAttribute.swift | 10 +- .../SyncCore_TelegramUserPresence.swift | 125 +- .../Messages/SearchMessages.swift | 32 +- .../Messages/TelegramEngineMessages.swift | 2 +- .../Sources/TelegramEngine/Peers/Peer.swift | 79 +- .../Peers/RequestUserPhotos.swift | 2 +- .../Stickers/TelegramEngineStickers.swift | 18 + .../Sources/Utils/MessageUtils.swift | 24 + .../Sources/PresenceStrings.swift | 8 +- .../ChatHistorySearchContainerNode.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 6 +- .../ChatMessageAttachedContentNode.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 6 +- .../ChatMessageContactBubbleContentNode.swift | 2 +- .../ChatMessageDateAndStatusNode.swift | 12 +- ...ChatMessageGiveawayBubbleContentNode.swift | 2 +- .../ChatMessageInstantVideoItemNode.swift | 4 +- .../ChatMessageInteractiveFileNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 2 +- .../ChatMessageInteractiveMediaNode.swift | 2 +- .../ChatMessageMapBubbleContentNode.swift | 2 +- .../ChatMessagePollBubbleContentNode.swift | 2 +- ...hatMessageReactionsFooterContentNode.swift | 4 +- ...atMessageRestrictedBubbleContentNode.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 6 +- .../ChatMessageTextBubbleContentNode.swift | 2 +- .../DustEffect/Metal/DustEffectShaders.metal | 29 + .../Sources/EmojiStatusComponent.swift | 11 + .../Sources/EmojiPagerContentComponent.swift | 1870 ---------------- .../Sources/EmojiPagerContentSignals.swift | 1950 +++++++++++++++++ .../Sources/ReactionChatPreviewItem.swift | 2 +- ...ChatControllerOpenMessageContextMenu.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 17 +- .../ChatSearchNavigationContentNode.swift | 4 +- .../ChatSearchResultsContollerNode.swift | 2 +- .../ChatSearchTitleAccessoryPanelNode.swift | 189 +- .../Sources/TopMessageReactions.swift | 86 +- .../WatchBridge/Sources/WatchBridge.swift | 2 +- 68 files changed, 4913 insertions(+), 3974 deletions(-) create mode 100644 submodules/TelegramCore/Sources/State/SavedMessageTags.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index a3f413e354..070fc2afdd 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -356,7 +356,7 @@ public enum ChatSearchDomain: Equatable { case everything case members case member(Peer) - case tag(String) + case tag(MessageReaction.Reaction, TelegramMediaFile) public static func ==(lhs: ChatSearchDomain, rhs: ChatSearchDomain) -> Bool { switch lhs { @@ -378,8 +378,8 @@ public enum ChatSearchDomain: Equatable { } else { return false } - case let .tag(tag): - if case .tag(tag) = rhs { + case let .tag(tag, file): + if case .tag(tag, file) = rhs { return true } else { return false diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 9911b6671e..0f8919416d 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1572,9 +1572,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let searchLocations: [SearchMessagesLocation] if let options = options { if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] } else if let (peerId, _, _) = options.peer { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1)] } else { if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] @@ -1584,7 +1584,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } else { if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: nil, maxDate: nil), .general(tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: nil, maxDate: nil), .general(tags: tagMask, minDate: nil, maxDate: nil)] } else if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: nil, maxDate: nil)] } else { diff --git a/submodules/PeerPresenceStatusManager/Sources/PeerPresenceStatusManager.swift b/submodules/PeerPresenceStatusManager/Sources/PeerPresenceStatusManager.swift index e173f2f2f4..5aa344478a 100644 --- a/submodules/PeerPresenceStatusManager/Sources/PeerPresenceStatusManager.swift +++ b/submodules/PeerPresenceStatusManager/Sources/PeerPresenceStatusManager.swift @@ -28,7 +28,7 @@ private func suggestedUserPresenceStringRefreshTimeout(_ presence: EnginePeer.Pr } else { return Double.infinity } - case .longTimeAgo, .lastWeek, .lastMonth: + case .longTimeAgo, .lastWeek, .lastMonth, .hidden: return Double.infinity } } diff --git a/submodules/SearchBarNode/BUILD b/submodules/SearchBarNode/BUILD index 5357b66545..1842fa27e0 100644 --- a/submodules/SearchBarNode/BUILD +++ b/submodules/SearchBarNode/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/ComponentFlow:ComponentFlow", "//submodules/AvatarNode:AvatarNode", "//submodules/AccountContext:AccountContext", + "//submodules/TelegramUI/Components/EmojiStatusComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index fff5306619..0285e9e6dc 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -9,6 +9,8 @@ import ActivityIndicator import AppBundle import AvatarNode import AccountContext +import ComponentFlow +import EmojiStatusComponent private func generateLoupeIcon(color: UIColor) -> UIImage? { return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: color) @@ -43,20 +45,24 @@ public struct SearchBarToken { } public let id: AnyHashable + public let context: AccountContext? public let icon: UIImage? public let iconOffset: CGFloat? public let peer: (EnginePeer, AccountContext, PresentationTheme)? public let isTag: Bool + public let emojiFile: TelegramMediaFile? public let title: String public let style: Style? public let permanent: Bool - public init(id: AnyHashable, icon: UIImage?, iconOffset: CGFloat? = 0.0, peer: (EnginePeer, AccountContext, PresentationTheme)? = nil, isTag: Bool = false, title: String, style: Style? = nil, permanent: Bool) { + public init(id: AnyHashable, context: AccountContext? = nil, icon: UIImage?, iconOffset: CGFloat? = 0.0, peer: (EnginePeer, AccountContext, PresentationTheme)? = nil, isTag: Bool = false, emojiFile: TelegramMediaFile? = nil, title: String, style: Style? = nil, permanent: Bool) { self.id = id + self.context = context self.icon = icon self.iconOffset = iconOffset self.peer = peer self.isTag = isTag + self.emojiFile = emojiFile self.title = title self.style = style self.permanent = permanent @@ -71,6 +77,7 @@ private final class TokenNode: ASDisplayNode { let titleNode: ASTextNode let backgroundNode: ASImageNode let avatarNode: AvatarNode? + var emojiView: ComponentView? var isSelected: Bool = false var isCollapsed: Bool = false @@ -204,6 +211,7 @@ private final class TokenNode: ASDisplayNode { height += 2.0 } + var emojiFileSize: CGSize? var leftInset: CGFloat = 3.0 if let icon = self.iconNode.image { leftInset += 1.0 @@ -214,9 +222,49 @@ private final class TokenNode: ASDisplayNode { transition.updateFrame(node: self.iconNode, frame: iconFrame) leftInset += icon.size.width + 3.0 } + if let emojiFile = self.token.emojiFile, let context = self.token.context { + let emojiView: ComponentView + if let current = self.emojiView { + emojiView = current + } else { + emojiView = ComponentView() + self.emojiView = emojiView + } + let emojiViewSize = emojiView.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + content: .animation( + content: .file(file: emojiFile), + size: CGSize(width: 32.0, height: 32.0), + placeholderColor: self.theme.primaryText.withMultipliedAlpha(0.2), + themeColor: self.theme.primaryText, + loopMode: .forever + ), + isVisibleForAnimations: false, + useSharedAnimation: true, + action: nil, + emojiFileUpdated: nil + )), + environment: {}, + containerSize: CGSize(width: 14.0, height: 14.0) + ) + if let emojiComponentView = emojiView.view { + if emojiComponentView.superview == nil { + self.containerNode.view.addSubview(emojiComponentView) + } + emojiComponentView.frame = CGRect(origin: CGPoint(x: leftInset + 2.0, y: floor((height - emojiViewSize.height) * 0.5)), size: emojiViewSize) + } + emojiFileSize = emojiViewSize + } if self.token.isTag { leftInset += 2.0 } + if let emojiFileSize { + leftInset += emojiFileSize.width + 7.0 + } let iconSize = self.token.icon?.size ?? CGSize() let titleSize = self.titleNode.measure(CGSize(width: constrainedSize.width - 6.0, height: constrainedSize.height)) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index d272aeb803..c67f98a298 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -597,6 +597,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[577659656] = { return Api.NotifyPeer.parse_notifyForumTopic($0) } dict[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) } dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) } + dict[1001931436] = { return Api.OutboxReadDate.parse_outboxReadDate($0) } dict[-1738178803] = { return Api.Page.parse_page($0) } dict[-837994576] = { return Api.PageBlock.parse_pageBlockAnchor($0) } dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) } @@ -752,6 +753,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1009288385] = { return Api.RichText.parse_textUrl($0) } dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) } dict[-1115174036] = { return Api.SavedDialog.parse_savedDialog($0) } + dict[-881854424] = { return Api.SavedReactionTag.parse_savedReactionTag($0) } dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) } dict[2137295719] = { return Api.SearchResultsPosition.parse_searchResultPosition($0) } dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) } @@ -962,6 +964,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1706939360] = { return Api.Update.parse_updateRecentStickers($0) } dict[-1364222348] = { return Api.Update.parse_updateSavedDialogPinned($0) } dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) } + dict[969307186] = { return Api.Update.parse_updateSavedReactionTags($0) } dict[1960361625] = { return Api.Update.parse_updateSavedRingtones($0) } dict[2103604867] = { return Api.Update.parse_updateSentStoryReaction($0) } dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) } @@ -996,6 +999,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) } dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) } + dict[-813865807] = { return Api.UserStatus.parse_userStatusHidden($0) } dict[2011940674] = { return Api.UserStatus.parse_userStatusLastMonth($0) } dict[129960444] = { return Api.UserStatus.parse_userStatusLastWeek($0) } dict[9203775] = { return Api.UserStatus.parse_userStatusOffline($0) } @@ -1180,6 +1184,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1153080793] = { return Api.messages.SavedDialogs.parse_savedDialogsSlice($0) } dict[-2069878259] = { return Api.messages.SavedGifs.parse_savedGifs($0) } dict[-402498398] = { return Api.messages.SavedGifs.parse_savedGifsNotModified($0) } + dict[844731658] = { return Api.messages.SavedReactionTags.parse_savedReactionTags($0) } + dict[-2003084817] = { return Api.messages.SavedReactionTags.parse_savedReactionTagsNotModified($0) } dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) } dict[343859772] = { return Api.messages.SearchResultsCalendar.parse_searchResultsCalendar($0) } dict[1404185519] = { return Api.messages.SearchResultsPositions.parse_searchResultsPositions($0) } @@ -1657,6 +1663,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.NotifyPeer: _1.serialize(buffer, boxed) + case let _1 as Api.OutboxReadDate: + _1.serialize(buffer, boxed) case let _1 as Api.Page: _1.serialize(buffer, boxed) case let _1 as Api.PageBlock: @@ -1761,6 +1769,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.SavedDialog: _1.serialize(buffer, boxed) + case let _1 as Api.SavedReactionTag: + _1.serialize(buffer, boxed) case let _1 as Api.SearchResultsCalendarPeriod: _1.serialize(buffer, boxed) case let _1 as Api.SearchResultsPosition: @@ -2083,6 +2093,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.SavedGifs: _1.serialize(buffer, boxed) + case let _1 as Api.messages.SavedReactionTags: + _1.serialize(buffer, boxed) case let _1 as Api.messages.SearchCounter: _1.serialize(buffer, boxed) case let _1 as Api.messages.SearchResultsCalendar: diff --git a/submodules/TelegramApi/Sources/Api15.swift b/submodules/TelegramApi/Sources/Api15.swift index d71cc7f9fe..278c569f5c 100644 --- a/submodules/TelegramApi/Sources/Api15.swift +++ b/submodules/TelegramApi/Sources/Api15.swift @@ -98,6 +98,42 @@ public extension Api { } } +public extension Api { + enum OutboxReadDate: TypeConstructorDescription { + case outboxReadDate(date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .outboxReadDate(let date): + if boxed { + buffer.appendInt32(1001931436) + } + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .outboxReadDate(let date): + return ("outboxReadDate", [("date", date as Any)]) + } + } + + public static func parse_outboxReadDate(_ reader: BufferReader) -> OutboxReadDate? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.OutboxReadDate.outboxReadDate(date: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum Page: TypeConstructorDescription { case page(flags: Int32, url: String, blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document], views: Int32?) diff --git a/submodules/TelegramApi/Sources/Api19.swift b/submodules/TelegramApi/Sources/Api19.swift index 61fa36ecb8..0f15508b76 100644 --- a/submodules/TelegramApi/Sources/Api19.swift +++ b/submodules/TelegramApi/Sources/Api19.swift @@ -652,6 +652,56 @@ public extension Api { } } +public extension Api { + enum SavedReactionTag: TypeConstructorDescription { + case savedReactionTag(flags: Int32, reaction: Api.Reaction, title: String?, count: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedReactionTag(let flags, let reaction, let title, let count): + if boxed { + buffer.appendInt32(-881854424) + } + serializeInt32(flags, buffer: buffer, boxed: false) + reaction.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + serializeInt32(count, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedReactionTag(let flags, let reaction, let title, let count): + return ("savedReactionTag", [("flags", flags as Any), ("reaction", reaction as Any), ("title", title as Any), ("count", count as Any)]) + } + } + + public static func parse_savedReactionTag(_ reader: BufferReader) -> SavedReactionTag? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Reaction? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Reaction + } + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.SavedReactionTag.savedReactionTag(flags: _1!, reaction: _2!, title: _3, count: _4!) + } + else { + return nil + } + } + + } +} public extension Api { enum SearchResultsCalendarPeriod: TypeConstructorDescription { case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32) @@ -972,59 +1022,3 @@ public extension Api { } } -public extension Api { - enum SecurePlainData: TypeConstructorDescription { - case securePlainEmail(email: String) - case securePlainPhone(phone: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .securePlainEmail(let email): - if boxed { - buffer.appendInt32(569137759) - } - serializeString(email, buffer: buffer, boxed: false) - break - case .securePlainPhone(let phone): - if boxed { - buffer.appendInt32(2103482845) - } - serializeString(phone, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .securePlainEmail(let email): - return ("securePlainEmail", [("email", email as Any)]) - case .securePlainPhone(let phone): - return ("securePlainPhone", [("phone", phone as Any)]) - } - } - - public static func parse_securePlainEmail(_ reader: BufferReader) -> SecurePlainData? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.SecurePlainData.securePlainEmail(email: _1!) - } - else { - return nil - } - } - public static func parse_securePlainPhone(_ reader: BufferReader) -> SecurePlainData? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.SecurePlainData.securePlainPhone(phone: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api20.swift b/submodules/TelegramApi/Sources/Api20.swift index 9100f36799..b3e42f92db 100644 --- a/submodules/TelegramApi/Sources/Api20.swift +++ b/submodules/TelegramApi/Sources/Api20.swift @@ -1,3 +1,59 @@ +public extension Api { + enum SecurePlainData: TypeConstructorDescription { + case securePlainEmail(email: String) + case securePlainPhone(phone: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .securePlainEmail(let email): + if boxed { + buffer.appendInt32(569137759) + } + serializeString(email, buffer: buffer, boxed: false) + break + case .securePlainPhone(let phone): + if boxed { + buffer.appendInt32(2103482845) + } + serializeString(phone, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .securePlainEmail(let email): + return ("securePlainEmail", [("email", email as Any)]) + case .securePlainPhone(let phone): + return ("securePlainPhone", [("phone", phone as Any)]) + } + } + + public static func parse_securePlainEmail(_ reader: BufferReader) -> SecurePlainData? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.SecurePlainData.securePlainEmail(email: _1!) + } + else { + return nil + } + } + public static func parse_securePlainPhone(_ reader: BufferReader) -> SecurePlainData? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.SecurePlainData.securePlainPhone(phone: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum SecureRequiredType: TypeConstructorDescription { case secureRequiredType(flags: Int32, type: Api.SecureValueType) diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 1242ceb8a0..1580f98d05 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -569,6 +569,7 @@ public extension Api { case updateRecentStickers case updateSavedDialogPinned(flags: Int32, peer: Api.DialogPeer) case updateSavedGifs + case updateSavedReactionTags case updateSavedRingtones case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction) case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity]) @@ -1534,6 +1535,12 @@ public extension Api { buffer.appendInt32(-1821035490) } + break + case .updateSavedReactionTags: + if boxed { + buffer.appendInt32(969307186) + } + break case .updateSavedRingtones: if boxed { @@ -1897,6 +1904,8 @@ public extension Api { return ("updateSavedDialogPinned", [("flags", flags as Any), ("peer", peer as Any)]) case .updateSavedGifs: return ("updateSavedGifs", []) + case .updateSavedReactionTags: + return ("updateSavedReactionTags", []) case .updateSavedRingtones: return ("updateSavedRingtones", []) case .updateSentStoryReaction(let peer, let storyId, let reaction): @@ -3848,6 +3857,9 @@ public extension Api { public static func parse_updateSavedGifs(_ reader: BufferReader) -> Update? { return Api.Update.updateSavedGifs } + public static func parse_updateSavedReactionTags(_ reader: BufferReader) -> Update? { + return Api.Update.updateSavedReactionTags + } public static func parse_updateSavedRingtones(_ reader: BufferReader) -> Update? { return Api.Update.updateSavedRingtones } diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index ae4e12ba96..c9d692ed24 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -801,6 +801,7 @@ public extension Api { public extension Api { enum UserStatus: TypeConstructorDescription { case userStatusEmpty + case userStatusHidden case userStatusLastMonth case userStatusLastWeek case userStatusOffline(wasOnline: Int32) @@ -814,6 +815,12 @@ public extension Api { buffer.appendInt32(164646985) } + break + case .userStatusHidden: + if boxed { + buffer.appendInt32(-813865807) + } + break case .userStatusLastMonth: if boxed { @@ -852,6 +859,8 @@ public extension Api { switch self { case .userStatusEmpty: return ("userStatusEmpty", []) + case .userStatusHidden: + return ("userStatusHidden", []) case .userStatusLastMonth: return ("userStatusLastMonth", []) case .userStatusLastWeek: @@ -868,6 +877,9 @@ public extension Api { public static func parse_userStatusEmpty(_ reader: BufferReader) -> UserStatus? { return Api.UserStatus.userStatusEmpty } + public static func parse_userStatusHidden(_ reader: BufferReader) -> UserStatus? { + return Api.UserStatus.userStatusHidden + } public static func parse_userStatusLastMonth(_ reader: BufferReader) -> UserStatus? { return Api.UserStatus.userStatusLastMonth } @@ -1382,207 +1394,3 @@ public extension Api { } } -public extension Api { - enum WebPage: TypeConstructorDescription { - case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) - case webPageEmpty(flags: Int32, id: Int64, url: String?) - case webPageNotModified(flags: Int32, cachedPageViews: Int32?) - case webPagePending(flags: Int32, id: Int64, url: String?, date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - if boxed { - buffer.appendInt32(-392411726) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - serializeString(displayUrl, buffer: buffer, boxed: false) - serializeInt32(hash, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} - if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} - if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes!.count)) - for item in attributes! { - item.serialize(buffer, true) - }} - break - case .webPageEmpty(let flags, let id, let url): - if boxed { - buffer.appendInt32(555358088) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - break - case .webPageNotModified(let flags, let cachedPageViews): - if boxed { - buffer.appendInt32(1930545681) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} - break - case .webPagePending(let flags, let id, let url, let date): - if boxed { - buffer.appendInt32(-1328464313) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): - return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) - case .webPageEmpty(let flags, let id, let url): - return ("webPageEmpty", [("flags", flags as Any), ("id", id as Any), ("url", url as Any)]) - case .webPageNotModified(let flags, let cachedPageViews): - return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) - case .webPagePending(let flags, let id, let url, let date): - return ("webPagePending", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("date", date as Any)]) - } - } - - public static func parse_webPage(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } - var _7: String? - if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } - var _8: String? - if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } - var _9: String? - if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } - var _10: Api.Photo? - if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _11: String? - if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } - var _12: String? - if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } - var _13: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } - var _14: Int32? - if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } - var _15: Int32? - if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } - var _16: String? - if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } - var _17: Api.Document? - if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.Document - } } - var _18: Api.Page? - if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { - _18 = Api.parse(reader, signature: signature) as? Api.Page - } } - var _19: [Api.WebPageAttribute]? - if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { - _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil - let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) - } - else { - return nil - } - } - public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) - } - else { - return nil - } - } - public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) - } - else { - return nil - } - } - public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 55a953bc11..918d392ea3 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,3 +1,207 @@ +public extension Api { + enum WebPage: TypeConstructorDescription { + case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?, attributes: [Api.WebPageAttribute]?) + case webPageEmpty(flags: Int32, id: Int64, url: String?) + case webPageNotModified(flags: Int32, cachedPageViews: Int32?) + case webPagePending(flags: Int32, id: Int64, url: String?, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): + if boxed { + buffer.appendInt32(-392411726) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeString(displayUrl, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(type!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(siteName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeString(description!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {photo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedUrl!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(embedType!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedWidth!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(embedHeight!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} + if Int(flags) & Int(1 << 12) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes!.count)) + for item in attributes! { + item.serialize(buffer, true) + }} + break + case .webPageEmpty(let flags, let id, let url): + if boxed { + buffer.appendInt32(555358088) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + break + case .webPageNotModified(let flags, let cachedPageViews): + if boxed { + buffer.appendInt32(1930545681) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(cachedPageViews!, buffer: buffer, boxed: false)} + break + case .webPagePending(let flags, let id, let url, let date): + if boxed { + buffer.appendInt32(-1328464313) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage, let attributes): + return ("webPage", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("displayUrl", displayUrl as Any), ("hash", hash as Any), ("type", type as Any), ("siteName", siteName as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("embedUrl", embedUrl as Any), ("embedType", embedType as Any), ("embedWidth", embedWidth as Any), ("embedHeight", embedHeight as Any), ("duration", duration as Any), ("author", author as Any), ("document", document as Any), ("cachedPage", cachedPage as Any), ("attributes", attributes as Any)]) + case .webPageEmpty(let flags, let id, let url): + return ("webPageEmpty", [("flags", flags as Any), ("id", id as Any), ("url", url as Any)]) + case .webPageNotModified(let flags, let cachedPageViews): + return ("webPageNotModified", [("flags", flags as Any), ("cachedPageViews", cachedPageViews as Any)]) + case .webPagePending(let flags, let id, let url, let date): + return ("webPagePending", [("flags", flags as Any), ("id", id as Any), ("url", url as Any), ("date", date as Any)]) + } + } + + public static func parse_webPage(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + var _7: String? + if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } + var _8: String? + if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) } + var _9: String? + if Int(_1!) & Int(1 << 3) != 0 {_9 = parseString(reader) } + var _10: Api.Photo? + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _11: String? + if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) } + var _12: String? + if Int(_1!) & Int(1 << 5) != 0 {_12 = parseString(reader) } + var _13: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_13 = reader.readInt32() } + var _14: Int32? + if Int(_1!) & Int(1 << 6) != 0 {_14 = reader.readInt32() } + var _15: Int32? + if Int(_1!) & Int(1 << 7) != 0 {_15 = reader.readInt32() } + var _16: String? + if Int(_1!) & Int(1 << 8) != 0 {_16 = parseString(reader) } + var _17: Api.Document? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _18: Api.Page? + if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.Page + } } + var _19: [Api.WebPageAttribute]? + if Int(_1!) & Int(1 << 12) != 0 {if let _ = reader.readInt32() { + _19 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebPageAttribute.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) + } + else { + return nil + } + } + public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) + } + else { + return nil + } + } + public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) + } + else { + return nil + } + } + public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) + } + else { + return nil + } + } + + } +} public extension Api { indirect enum WebPageAttribute: TypeConstructorDescription { case webPageAttributeStory(flags: Int32, peer: Api.Peer, id: Int32, story: Api.StoryItem?) @@ -1140,153 +1344,3 @@ public extension Api.account { } } -public extension Api.account { - enum TmpPassword: TypeConstructorDescription { - case tmpPassword(tmpPassword: Buffer, validUntil: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .tmpPassword(let tmpPassword, let validUntil): - if boxed { - buffer.appendInt32(-614138572) - } - serializeBytes(tmpPassword, buffer: buffer, boxed: false) - serializeInt32(validUntil, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .tmpPassword(let tmpPassword, let validUntil): - return ("tmpPassword", [("tmpPassword", tmpPassword as Any), ("validUntil", validUntil as Any)]) - } - } - - public static func parse_tmpPassword(_ reader: BufferReader) -> TmpPassword? { - var _1: Buffer? - _1 = parseBytes(reader) - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.account { - enum WallPapers: TypeConstructorDescription { - case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) - case wallPapersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .wallPapers(let hash, let wallpapers): - if boxed { - buffer.appendInt32(-842824308) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(wallpapers.count)) - for item in wallpapers { - item.serialize(buffer, true) - } - break - case .wallPapersNotModified: - if boxed { - buffer.appendInt32(471437699) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .wallPapers(let hash, let wallpapers): - return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) - case .wallPapersNotModified: - return ("wallPapersNotModified", []) - } - } - - public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.WallPaper]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) - } - else { - return nil - } - } - public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { - return Api.account.WallPapers.wallPapersNotModified - } - - } -} -public extension Api.account { - enum WebAuthorizations: TypeConstructorDescription { - case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .webAuthorizations(let authorizations, let users): - if boxed { - buffer.appendInt32(-313079300) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(authorizations.count)) - for item in authorizations { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .webAuthorizations(let authorizations, let users): - return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) - } - } - - public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { - var _1: [Api.WebAuthorization]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 95534e002c..babd3e977a 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,153 @@ +public extension Api.account { + enum TmpPassword: TypeConstructorDescription { + case tmpPassword(tmpPassword: Buffer, validUntil: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .tmpPassword(let tmpPassword, let validUntil): + if boxed { + buffer.appendInt32(-614138572) + } + serializeBytes(tmpPassword, buffer: buffer, boxed: false) + serializeInt32(validUntil, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .tmpPassword(let tmpPassword, let validUntil): + return ("tmpPassword", [("tmpPassword", tmpPassword as Any), ("validUntil", validUntil as Any)]) + } + } + + public static func parse_tmpPassword(_ reader: BufferReader) -> TmpPassword? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.account { + enum WallPapers: TypeConstructorDescription { + case wallPapers(hash: Int64, wallpapers: [Api.WallPaper]) + case wallPapersNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .wallPapers(let hash, let wallpapers): + if boxed { + buffer.appendInt32(-842824308) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(wallpapers.count)) + for item in wallpapers { + item.serialize(buffer, true) + } + break + case .wallPapersNotModified: + if boxed { + buffer.appendInt32(471437699) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .wallPapers(let hash, let wallpapers): + return ("wallPapers", [("hash", hash as Any), ("wallpapers", wallpapers as Any)]) + case .wallPapersNotModified: + return ("wallPapersNotModified", []) + } + } + + public static func parse_wallPapers(_ reader: BufferReader) -> WallPapers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.WallPaper]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WallPaper.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) + } + else { + return nil + } + } + public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { + return Api.account.WallPapers.wallPapersNotModified + } + + } +} +public extension Api.account { + enum WebAuthorizations: TypeConstructorDescription { + case webAuthorizations(authorizations: [Api.WebAuthorization], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .webAuthorizations(let authorizations, let users): + if boxed { + buffer.appendInt32(-313079300) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(authorizations.count)) + for item in authorizations { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .webAuthorizations(let authorizations, let users): + return ("webAuthorizations", [("authorizations", authorizations as Any), ("users", users as Any)]) + } + } + + public static func parse_webAuthorizations(_ reader: BufferReader) -> WebAuthorizations? { + var _1: [Api.WebAuthorization]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.WebAuthorization.self) + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) + } + else { + return nil + } + } + + } +} public extension Api.auth { enum Authorization: TypeConstructorDescription { case authorization(flags: Int32, otherwiseReloginDays: Int32?, tmpSessions: Int32?, futureAuthToken: Buffer?, user: Api.User) @@ -912,199 +1062,3 @@ public extension Api.channels { } } -public extension Api.channels { - enum SendAsPeers: TypeConstructorDescription { - case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sendAsPeers(let peers, let chats, let users): - if boxed { - buffer.appendInt32(-191450938) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sendAsPeers(let peers, let chats, let users): - return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { - var _1: [Api.SendAsPeer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.chatlists { - enum ChatlistInvite: TypeConstructorDescription { - case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): - if boxed { - buffer.appendInt32(500007837) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): - if boxed { - buffer.appendInt32(-91752871) - } - serializeInt32(filterId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(missingPeers.count)) - for item in missingPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(alreadyPeers.count)) - for item in alreadyPeers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): - return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): - return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: [Api.Peer]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) - } - else { - return nil - } - } - public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Peer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _3: [Api.Peer]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 2e926c8b0f..8e235dee51 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,3 +1,199 @@ +public extension Api.channels { + enum SendAsPeers: TypeConstructorDescription { + case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sendAsPeers(let peers, let chats, let users): + if boxed { + buffer.appendInt32(-191450938) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sendAsPeers(let peers, let chats, let users): + return ("sendAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? { + var _1: [Api.SendAsPeer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.chatlists { + enum ChatlistInvite: TypeConstructorDescription { + case chatlistInvite(flags: Int32, title: String, emoticon: String?, peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + case chatlistInviteAlready(filterId: Int32, missingPeers: [Api.Peer], alreadyPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + if boxed { + buffer.appendInt32(500007837) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + if boxed { + buffer.appendInt32(-91752871) + } + serializeInt32(filterId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(missingPeers.count)) + for item in missingPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(alreadyPeers.count)) + for item in alreadyPeers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatlistInvite(let flags, let title, let emoticon, let peers, let chats, let users): + return ("chatlistInvite", [("flags", flags as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + case .chatlistInviteAlready(let filterId, let missingPeers, let alreadyPeers, let chats, let users): + return ("chatlistInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("alreadyPeers", alreadyPeers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_chatlistInvite(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: [Api.Peer]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) + } + else { + return nil + } + } + public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Peer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _3: [Api.Peer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + + } +} public extension Api.chatlists { enum ChatlistUpdates: TypeConstructorDescription { case chatlistUpdates(missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) @@ -1254,149 +1450,3 @@ public extension Api.help { } } -public extension Api.help { - enum PeerColorSet: TypeConstructorDescription { - case peerColorProfileSet(paletteColors: [Int32], bgColors: [Int32], storyColors: [Int32]) - case peerColorSet(colors: [Int32]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): - if boxed { - buffer.appendInt32(1987928555) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(paletteColors.count)) - for item in paletteColors { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(bgColors.count)) - for item in bgColors { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(storyColors.count)) - for item in storyColors { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .peerColorSet(let colors): - if boxed { - buffer.appendInt32(639736408) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(colors.count)) - for item in colors { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): - return ("peerColorProfileSet", [("paletteColors", paletteColors as Any), ("bgColors", bgColors as Any), ("storyColors", storyColors as Any)]) - case .peerColorSet(let colors): - return ("peerColorSet", [("colors", colors as Any)]) - } - } - - public static func parse_peerColorProfileSet(_ reader: BufferReader) -> PeerColorSet? { - var _1: [Int32]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - var _2: [Int32]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - var _3: [Int32]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.PeerColorSet.peerColorProfileSet(paletteColors: _1!, bgColors: _2!, storyColors: _3!) - } - else { - return nil - } - } - public static func parse_peerColorSet(_ reader: BufferReader) -> PeerColorSet? { - var _1: [Int32]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.help.PeerColorSet.peerColorSet(colors: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.help { - enum PeerColors: TypeConstructorDescription { - case peerColors(hash: Int32, colors: [Api.help.PeerColorOption]) - case peerColorsNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .peerColors(let hash, let colors): - if boxed { - buffer.appendInt32(16313608) - } - serializeInt32(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(colors.count)) - for item in colors { - item.serialize(buffer, true) - } - break - case .peerColorsNotModified: - if boxed { - buffer.appendInt32(732034510) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .peerColors(let hash, let colors): - return ("peerColors", [("hash", hash as Any), ("colors", colors as Any)]) - case .peerColorsNotModified: - return ("peerColorsNotModified", []) - } - } - - public static func parse_peerColors(_ reader: BufferReader) -> PeerColors? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.help.PeerColorOption]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.PeerColorOption.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.PeerColors.peerColors(hash: _1!, colors: _2!) - } - else { - return nil - } - } - public static func parse_peerColorsNotModified(_ reader: BufferReader) -> PeerColors? { - return Api.help.PeerColors.peerColorsNotModified - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index badbd70145..e06a09df65 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,3 +1,149 @@ +public extension Api.help { + enum PeerColorSet: TypeConstructorDescription { + case peerColorProfileSet(paletteColors: [Int32], bgColors: [Int32], storyColors: [Int32]) + case peerColorSet(colors: [Int32]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): + if boxed { + buffer.appendInt32(1987928555) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(paletteColors.count)) + for item in paletteColors { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(bgColors.count)) + for item in bgColors { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(storyColors.count)) + for item in storyColors { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .peerColorSet(let colors): + if boxed { + buffer.appendInt32(639736408) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(colors.count)) + for item in colors { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerColorProfileSet(let paletteColors, let bgColors, let storyColors): + return ("peerColorProfileSet", [("paletteColors", paletteColors as Any), ("bgColors", bgColors as Any), ("storyColors", storyColors as Any)]) + case .peerColorSet(let colors): + return ("peerColorSet", [("colors", colors as Any)]) + } + } + + public static func parse_peerColorProfileSet(_ reader: BufferReader) -> PeerColorSet? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _3: [Int32]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.help.PeerColorSet.peerColorProfileSet(paletteColors: _1!, bgColors: _2!, storyColors: _3!) + } + else { + return nil + } + } + public static func parse_peerColorSet(_ reader: BufferReader) -> PeerColorSet? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.help.PeerColorSet.peerColorSet(colors: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.help { + enum PeerColors: TypeConstructorDescription { + case peerColors(hash: Int32, colors: [Api.help.PeerColorOption]) + case peerColorsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerColors(let hash, let colors): + if boxed { + buffer.appendInt32(16313608) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(colors.count)) + for item in colors { + item.serialize(buffer, true) + } + break + case .peerColorsNotModified: + if boxed { + buffer.appendInt32(732034510) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerColors(let hash, let colors): + return ("peerColors", [("hash", hash as Any), ("colors", colors as Any)]) + case .peerColorsNotModified: + return ("peerColorsNotModified", []) + } + } + + public static func parse_peerColors(_ reader: BufferReader) -> PeerColors? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.help.PeerColorOption]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.PeerColorOption.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.help.PeerColors.peerColors(hash: _1!, colors: _2!) + } + else { + return nil + } + } + public static func parse_peerColorsNotModified(_ reader: BufferReader) -> PeerColors? { + return Api.help.PeerColors.peerColorsNotModified + } + + } +} public extension Api.help { enum PremiumPromo: TypeConstructorDescription { case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User]) @@ -1250,223 +1396,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum DhConfig: TypeConstructorDescription { - case dhConfig(g: Int32, p: Buffer, version: Int32, random: Buffer) - case dhConfigNotModified(random: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .dhConfig(let g, let p, let version, let random): - if boxed { - buffer.appendInt32(740433629) - } - serializeInt32(g, buffer: buffer, boxed: false) - serializeBytes(p, buffer: buffer, boxed: false) - serializeInt32(version, buffer: buffer, boxed: false) - serializeBytes(random, buffer: buffer, boxed: false) - break - case .dhConfigNotModified(let random): - if boxed { - buffer.appendInt32(-1058912715) - } - serializeBytes(random, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .dhConfig(let g, let p, let version, let random): - return ("dhConfig", [("g", g as Any), ("p", p as Any), ("version", version as Any), ("random", random as Any)]) - case .dhConfigNotModified(let random): - return ("dhConfigNotModified", [("random", random as Any)]) - } - } - - public static func parse_dhConfig(_ reader: BufferReader) -> DhConfig? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Int32? - _3 = reader.readInt32() - var _4: Buffer? - _4 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) - } - else { - return nil - } - } - public static func parse_dhConfigNotModified(_ reader: BufferReader) -> DhConfig? { - var _1: Buffer? - _1 = parseBytes(reader) - let _c1 = _1 != nil - if _c1 { - return Api.messages.DhConfig.dhConfigNotModified(random: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum Dialogs: TypeConstructorDescription { - case dialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - case dialogsNotModified(count: Int32) - case dialogsSlice(count: Int32, dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .dialogs(let dialogs, let messages, let chats, let users): - if boxed { - buffer.appendInt32(364538944) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .dialogsNotModified(let count): - if boxed { - buffer.appendInt32(-253500010) - } - serializeInt32(count, buffer: buffer, boxed: false) - break - case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): - if boxed { - buffer.appendInt32(1910543603) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .dialogs(let dialogs, let messages, let chats, let users): - return ("dialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .dialogsNotModified(let count): - return ("dialogsNotModified", [("count", count as Any)]) - case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): - return ("dialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_dialogs(_ reader: BufferReader) -> Dialogs? { - var _1: [Api.Dialog]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) - } - var _2: [Api.Message]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - public static func parse_dialogsNotModified(_ reader: BufferReader) -> Dialogs? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.Dialogs.dialogsNotModified(count: _1!) - } - else { - return nil - } - } - public static func parse_dialogsSlice(_ reader: BufferReader) -> Dialogs? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.Dialog]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) - } - var _3: [Api.Message]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 639e18a7b2..ac6190e6f2 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1,3 +1,223 @@ +public extension Api.messages { + enum DhConfig: TypeConstructorDescription { + case dhConfig(g: Int32, p: Buffer, version: Int32, random: Buffer) + case dhConfigNotModified(random: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .dhConfig(let g, let p, let version, let random): + if boxed { + buffer.appendInt32(740433629) + } + serializeInt32(g, buffer: buffer, boxed: false) + serializeBytes(p, buffer: buffer, boxed: false) + serializeInt32(version, buffer: buffer, boxed: false) + serializeBytes(random, buffer: buffer, boxed: false) + break + case .dhConfigNotModified(let random): + if boxed { + buffer.appendInt32(-1058912715) + } + serializeBytes(random, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .dhConfig(let g, let p, let version, let random): + return ("dhConfig", [("g", g as Any), ("p", p as Any), ("version", version as Any), ("random", random as Any)]) + case .dhConfigNotModified(let random): + return ("dhConfigNotModified", [("random", random as Any)]) + } + } + + public static func parse_dhConfig(_ reader: BufferReader) -> DhConfig? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Int32? + _3 = reader.readInt32() + var _4: Buffer? + _4 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) + } + else { + return nil + } + } + public static func parse_dhConfigNotModified(_ reader: BufferReader) -> DhConfig? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.messages.DhConfig.dhConfigNotModified(random: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum Dialogs: TypeConstructorDescription { + case dialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case dialogsNotModified(count: Int32) + case dialogsSlice(count: Int32, dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .dialogs(let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(364538944) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .dialogsNotModified(let count): + if boxed { + buffer.appendInt32(-253500010) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(1910543603) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .dialogs(let dialogs, let messages, let chats, let users): + return ("dialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .dialogsNotModified(let count): + return ("dialogsNotModified", [("count", count as Any)]) + case .dialogsSlice(let count, let dialogs, let messages, let chats, let users): + return ("dialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_dialogs(_ reader: BufferReader) -> Dialogs? { + var _1: [Api.Dialog]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) + } + var _2: [Api.Message]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) + } + else { + return nil + } + } + public static func parse_dialogsNotModified(_ reader: BufferReader) -> Dialogs? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.Dialogs.dialogsNotModified(count: _1!) + } + else { + return nil + } + } + public static func parse_dialogsSlice(_ reader: BufferReader) -> Dialogs? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Dialog]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Dialog.self) + } + var _3: [Api.Message]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum DiscussionMessage: TypeConstructorDescription { case discussionMessage(flags: Int32, messages: [Api.Message], maxId: Int32?, readInboxMaxId: Int32?, readOutboxMaxId: Int32?, unreadCount: Int32, chats: [Api.Chat], users: [Api.User]) @@ -1346,233 +1566,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum RecentStickers: TypeConstructorDescription { - case recentStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document], dates: [Int32]) - case recentStickersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .recentStickers(let hash, let packs, let stickers, let dates): - if boxed { - buffer.appendInt32(-1999405994) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dates.count)) - for item in dates { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - case .recentStickersNotModified: - if boxed { - buffer.appendInt32(186120336) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .recentStickers(let hash, let packs, let stickers, let dates): - return ("recentStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any), ("dates", dates as Any)]) - case .recentStickersNotModified: - return ("recentStickersNotModified", []) - } - } - - public static func parse_recentStickers(_ reader: BufferReader) -> RecentStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.Document]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - var _4: [Int32]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) - } - else { - return nil - } - } - public static func parse_recentStickersNotModified(_ reader: BufferReader) -> RecentStickers? { - return Api.messages.RecentStickers.recentStickersNotModified - } - - } -} -public extension Api.messages { - enum SavedDialogs: TypeConstructorDescription { - case savedDialogs(dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - case savedDialogsNotModified(count: Int32) - case savedDialogsSlice(count: Int32, dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .savedDialogs(let dialogs, let messages, let chats, let users): - if boxed { - buffer.appendInt32(-130358751) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .savedDialogsNotModified(let count): - if boxed { - buffer.appendInt32(-1071681560) - } - serializeInt32(count, buffer: buffer, boxed: false) - break - case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): - if boxed { - buffer.appendInt32(1153080793) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(dialogs.count)) - for item in dialogs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedDialogs(let dialogs, let messages, let chats, let users): - return ("savedDialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - case .savedDialogsNotModified(let count): - return ("savedDialogsNotModified", [("count", count as Any)]) - case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): - return ("savedDialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_savedDialogs(_ reader: BufferReader) -> SavedDialogs? { - var _1: [Api.SavedDialog]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) - } - var _2: [Api.Message]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.SavedDialogs.savedDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - public static func parse_savedDialogsNotModified(_ reader: BufferReader) -> SavedDialogs? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.SavedDialogs.savedDialogsNotModified(count: _1!) - } - else { - return nil - } - } - public static func parse_savedDialogsSlice(_ reader: BufferReader) -> SavedDialogs? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.SavedDialog]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) - } - var _3: [Api.Message]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.SavedDialogs.savedDialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index c0a8e8f120..fa6a398716 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -1,3 +1,233 @@ +public extension Api.messages { + enum RecentStickers: TypeConstructorDescription { + case recentStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document], dates: [Int32]) + case recentStickersNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .recentStickers(let hash, let packs, let stickers, let dates): + if boxed { + buffer.appendInt32(-1999405994) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dates.count)) + for item in dates { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .recentStickersNotModified: + if boxed { + buffer.appendInt32(186120336) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .recentStickers(let hash, let packs, let stickers, let dates): + return ("recentStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any), ("dates", dates as Any)]) + case .recentStickersNotModified: + return ("recentStickersNotModified", []) + } + } + + public static func parse_recentStickers(_ reader: BufferReader) -> RecentStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.Document]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + var _4: [Int32]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) + } + else { + return nil + } + } + public static func parse_recentStickersNotModified(_ reader: BufferReader) -> RecentStickers? { + return Api.messages.RecentStickers.recentStickersNotModified + } + + } +} +public extension Api.messages { + enum SavedDialogs: TypeConstructorDescription { + case savedDialogs(dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case savedDialogsNotModified(count: Int32) + case savedDialogsSlice(count: Int32, dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedDialogs(let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-130358751) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .savedDialogsNotModified(let count): + if boxed { + buffer.appendInt32(-1071681560) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(1153080793) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedDialogs(let dialogs, let messages, let chats, let users): + return ("savedDialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .savedDialogsNotModified(let count): + return ("savedDialogsNotModified", [("count", count as Any)]) + case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): + return ("savedDialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_savedDialogs(_ reader: BufferReader) -> SavedDialogs? { + var _1: [Api.SavedDialog]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) + } + var _2: [Api.Message]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.SavedDialogs.savedDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) + } + else { + return nil + } + } + public static func parse_savedDialogsNotModified(_ reader: BufferReader) -> SavedDialogs? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.SavedDialogs.savedDialogsNotModified(count: _1!) + } + else { + return nil + } + } + public static func parse_savedDialogsSlice(_ reader: BufferReader) -> SavedDialogs? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.SavedDialog]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) + } + var _3: [Api.Message]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.SavedDialogs.savedDialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum SavedGifs: TypeConstructorDescription { case savedGifs(hash: Int64, gifs: [Api.Document]) @@ -56,6 +286,64 @@ public extension Api.messages { } } +public extension Api.messages { + enum SavedReactionTags: TypeConstructorDescription { + case savedReactionTags(tags: [Api.SavedReactionTag], hash: Int64) + case savedReactionTagsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedReactionTags(let tags, let hash): + if boxed { + buffer.appendInt32(844731658) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(tags.count)) + for item in tags { + item.serialize(buffer, true) + } + serializeInt64(hash, buffer: buffer, boxed: false) + break + case .savedReactionTagsNotModified: + if boxed { + buffer.appendInt32(-2003084817) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedReactionTags(let tags, let hash): + return ("savedReactionTags", [("tags", tags as Any), ("hash", hash as Any)]) + case .savedReactionTagsNotModified: + return ("savedReactionTagsNotModified", []) + } + } + + public static func parse_savedReactionTags(_ reader: BufferReader) -> SavedReactionTags? { + var _1: [Api.SavedReactionTag]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedReactionTag.self) + } + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SavedReactionTags.savedReactionTags(tags: _1!, hash: _2!) + } + else { + return nil + } + } + public static func parse_savedReactionTagsNotModified(_ reader: BufferReader) -> SavedReactionTags? { + return Api.messages.SavedReactionTags.savedReactionTagsNotModified + } + + } +} public extension Api.messages { enum SearchCounter: TypeConstructorDescription { case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32) @@ -1176,367 +1464,3 @@ public extension Api.payments { } } -public extension Api.payments { - enum PaymentReceipt: TypeConstructorDescription { - case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - if boxed { - buffer.appendInt32(1891958275) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - serializeString(credentialsTitle, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) - } - } - - public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: String? - _6 = parseString(reader) - var _7: Api.WebDocument? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _8: Api.Invoice? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _9: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _10: Api.ShippingOption? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption - } } - var _11: Int64? - if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } - var _12: String? - _12 = parseString(reader) - var _13: Int64? - _13 = reader.readInt64() - var _14: String? - _14 = parseString(reader) - var _15: [Api.User]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - indirect enum PaymentResult: TypeConstructorDescription { - case paymentResult(updates: Api.Updates) - case paymentVerificationNeeded(url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentResult(let updates): - if boxed { - buffer.appendInt32(1314881805) - } - updates.serialize(buffer, true) - break - case .paymentVerificationNeeded(let url): - if boxed { - buffer.appendInt32(-666824391) - } - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .paymentResult(let updates): - return ("paymentResult", [("updates", updates as Any)]) - case .paymentVerificationNeeded(let url): - return ("paymentVerificationNeeded", [("url", url as Any)]) - } - } - - public static func parse_paymentResult(_ reader: BufferReader) -> PaymentResult? { - var _1: Api.Updates? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Updates - } - let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentResult(updates: _1!) - } - else { - return nil - } - } - public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum SavedInfo: TypeConstructorDescription { - case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .savedInfo(let flags, let savedInfo): - if boxed { - buffer.appendInt32(-74456004) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedInfo(let flags, let savedInfo): - return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) - } - } - - public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum ValidatedRequestedInfo: TypeConstructorDescription { - case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .validatedRequestedInfo(let flags, let id, let shippingOptions): - if boxed { - buffer.appendInt32(-784000893) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(id!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(shippingOptions!.count)) - for item in shippingOptions! { - item.serialize(buffer, true) - }} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .validatedRequestedInfo(let flags, let id, let shippingOptions): - return ("validatedRequestedInfo", [("flags", flags as Any), ("id", id as Any), ("shippingOptions", shippingOptions as Any)]) - } - } - - public static func parse_validatedRequestedInfo(_ reader: BufferReader) -> ValidatedRequestedInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } - var _3: [Api.ShippingOption]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ShippingOption.self) - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum ExportedGroupCallInvite: TypeConstructorDescription { - case exportedGroupCallInvite(link: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .exportedGroupCallInvite(let link): - if boxed { - buffer.appendInt32(541839704) - } - serializeString(link, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .exportedGroupCallInvite(let link): - return ("exportedGroupCallInvite", [("link", link as Any)]) - } - } - - public static func parse_exportedGroupCallInvite(_ reader: BufferReader) -> ExportedGroupCallInvite? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum GroupCall: TypeConstructorDescription { - case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): - if boxed { - buffer.appendInt32(-1636664659) - } - call.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(participants.count)) - for item in participants { - item.serialize(buffer, true) - } - serializeString(participantsNextOffset, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): - return ("groupCall", [("call", call as Any), ("participants", participants as Any), ("participantsNextOffset", participantsNextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? { - var _1: Api.GroupCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.GroupCall - } - var _2: [Api.GroupCallParticipant]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) - } - var _3: String? - _3 = parseString(reader) - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 472f613cd6..ae223bc1d7 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1,3 +1,367 @@ +public extension Api.payments { + enum PaymentReceipt: TypeConstructorDescription { + case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + if boxed { + buffer.appendInt32(1891958275) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + serializeString(credentialsTitle, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) + } + } + + public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + var _7: Api.WebDocument? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _8: Api.Invoice? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _9: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _10: Api.ShippingOption? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption + } } + var _11: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } + var _12: String? + _12 = parseString(reader) + var _13: Int64? + _13 = reader.readInt64() + var _14: String? + _14 = parseString(reader) + var _15: [Api.User]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + indirect enum PaymentResult: TypeConstructorDescription { + case paymentResult(updates: Api.Updates) + case paymentVerificationNeeded(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentResult(let updates): + if boxed { + buffer.appendInt32(1314881805) + } + updates.serialize(buffer, true) + break + case .paymentVerificationNeeded(let url): + if boxed { + buffer.appendInt32(-666824391) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentResult(let updates): + return ("paymentResult", [("updates", updates as Any)]) + case .paymentVerificationNeeded(let url): + return ("paymentVerificationNeeded", [("url", url as Any)]) + } + } + + public static func parse_paymentResult(_ reader: BufferReader) -> PaymentResult? { + var _1: Api.Updates? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Updates + } + let _c1 = _1 != nil + if _c1 { + return Api.payments.PaymentResult.paymentResult(updates: _1!) + } + else { + return nil + } + } + public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum SavedInfo: TypeConstructorDescription { + case savedInfo(flags: Int32, savedInfo: Api.PaymentRequestedInfo?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedInfo(let flags, let savedInfo): + if boxed { + buffer.appendInt32(-74456004) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedInfo(let flags, let savedInfo): + return ("savedInfo", [("flags", flags as Any), ("savedInfo", savedInfo as Any)]) + } + } + + public static func parse_savedInfo(_ reader: BufferReader) -> SavedInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum ValidatedRequestedInfo: TypeConstructorDescription { + case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .validatedRequestedInfo(let flags, let id, let shippingOptions): + if boxed { + buffer.appendInt32(-784000893) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(id!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(shippingOptions!.count)) + for item in shippingOptions! { + item.serialize(buffer, true) + }} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .validatedRequestedInfo(let flags, let id, let shippingOptions): + return ("validatedRequestedInfo", [("flags", flags as Any), ("id", id as Any), ("shippingOptions", shippingOptions as Any)]) + } + } + + public static func parse_validatedRequestedInfo(_ reader: BufferReader) -> ValidatedRequestedInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } + var _3: [Api.ShippingOption]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ShippingOption.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum ExportedGroupCallInvite: TypeConstructorDescription { + case exportedGroupCallInvite(link: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .exportedGroupCallInvite(let link): + if boxed { + buffer.appendInt32(541839704) + } + serializeString(link, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .exportedGroupCallInvite(let link): + return ("exportedGroupCallInvite", [("link", link as Any)]) + } + } + + public static func parse_exportedGroupCallInvite(_ reader: BufferReader) -> ExportedGroupCallInvite? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum GroupCall: TypeConstructorDescription { + case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): + if boxed { + buffer.appendInt32(-1636664659) + } + call.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(participants.count)) + for item in participants { + item.serialize(buffer, true) + } + serializeString(participantsNextOffset, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users): + return ("groupCall", [("call", call as Any), ("participants", participants as Any), ("participantsNextOffset", participantsNextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? { + var _1: Api.GroupCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.GroupCall + } + var _2: [Api.GroupCallParticipant]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self) + } + var _3: String? + _3 = parseString(reader) + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) + } + else { + return nil + } + } + + } +} public extension Api.phone { enum GroupCallStreamChannels: TypeConstructorDescription { case groupCallStreamChannels(channels: [Api.GroupCallStreamChannel]) @@ -1268,311 +1632,3 @@ public extension Api.storage { } } -public extension Api.stories { - enum AllStories: TypeConstructorDescription { - case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode) - case allStoriesNotModified(flags: Int32, state: String, stealthMode: Api.StoriesStealthMode) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): - if boxed { - buffer.appendInt32(1862033025) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - serializeString(state, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peerStories.count)) - for item in peerStories { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - stealthMode.serialize(buffer, true) - break - case .allStoriesNotModified(let flags, let state, let stealthMode): - if boxed { - buffer.appendInt32(291044926) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(state, buffer: buffer, boxed: false) - stealthMode.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): - return ("allStories", [("flags", flags as Any), ("count", count as Any), ("state", state as Any), ("peerStories", peerStories as Any), ("chats", chats as Any), ("users", users as Any), ("stealthMode", stealthMode as Any)]) - case .allStoriesNotModified(let flags, let state, let stealthMode): - return ("allStoriesNotModified", [("flags", flags as Any), ("state", state as Any), ("stealthMode", stealthMode as Any)]) - } - } - - public static func parse_allStories(_ reader: BufferReader) -> AllStories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: String? - _3 = parseString(reader) - var _4: [Api.PeerStories]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerStories.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _7: Api.StoriesStealthMode? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) - } - else { - return nil - } - } - public static func parse_allStoriesNotModified(_ reader: BufferReader) -> AllStories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Api.StoriesStealthMode? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum PeerStories: TypeConstructorDescription { - case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .peerStories(let stories, let chats, let users): - if boxed { - buffer.appendInt32(-890861720) - } - stories.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .peerStories(let stories, let chats, let users): - return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { - var _1: Api.PeerStories? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PeerStories - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum Stories: TypeConstructorDescription { - case stories(count: Int32, stories: [Api.StoryItem], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stories(let count, let stories, let chats, let users): - if boxed { - buffer.appendInt32(1574486984) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stories.count)) - for item in stories { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stories(let count, let stories, let chats, let users): - return ("stories", [("count", count as Any), ("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_stories(_ reader: BufferReader) -> Stories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.StoryItem]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) - } - var _3: [Api.Chat]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _4: [Api.User]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.stories.Stories.stories(count: _1!, stories: _2!, chats: _3!, users: _4!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum StoryReactionsList: TypeConstructorDescription { - case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): - if boxed { - buffer.appendInt32(-1436583780) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reactions.count)) - for item in reactions { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): - return ("storyReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) - } - } - - public static func parse_storyReactionsList(_ reader: BufferReader) -> StoryReactionsList? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.StoryReaction]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryReaction.self) - } - var _4: [Api.Chat]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _6: String? - if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index e5b0c6e49a..2b161fbd97 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -1,3 +1,311 @@ +public extension Api.stories { + enum AllStories: TypeConstructorDescription { + case allStories(flags: Int32, count: Int32, state: String, peerStories: [Api.PeerStories], chats: [Api.Chat], users: [Api.User], stealthMode: Api.StoriesStealthMode) + case allStoriesNotModified(flags: Int32, state: String, stealthMode: Api.StoriesStealthMode) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): + if boxed { + buffer.appendInt32(1862033025) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + serializeString(state, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peerStories.count)) + for item in peerStories { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + stealthMode.serialize(buffer, true) + break + case .allStoriesNotModified(let flags, let state, let stealthMode): + if boxed { + buffer.appendInt32(291044926) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(state, buffer: buffer, boxed: false) + stealthMode.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .allStories(let flags, let count, let state, let peerStories, let chats, let users, let stealthMode): + return ("allStories", [("flags", flags as Any), ("count", count as Any), ("state", state as Any), ("peerStories", peerStories as Any), ("chats", chats as Any), ("users", users as Any), ("stealthMode", stealthMode as Any)]) + case .allStoriesNotModified(let flags, let state, let stealthMode): + return ("allStoriesNotModified", [("flags", flags as Any), ("state", state as Any), ("stealthMode", stealthMode as Any)]) + } + } + + public static func parse_allStories(_ reader: BufferReader) -> AllStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: [Api.PeerStories]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerStories.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _7: Api.StoriesStealthMode? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) + } + else { + return nil + } + } + public static func parse_allStoriesNotModified(_ reader: BufferReader) -> AllStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Api.StoriesStealthMode? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum PeerStories: TypeConstructorDescription { + case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerStories(let stories, let chats, let users): + if boxed { + buffer.appendInt32(-890861720) + } + stories.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerStories(let stories, let chats, let users): + return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { + var _1: Api.PeerStories? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PeerStories + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum Stories: TypeConstructorDescription { + case stories(count: Int32, stories: [Api.StoryItem], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stories(let count, let stories, let chats, let users): + if boxed { + buffer.appendInt32(1574486984) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stories(let count, let stories, let chats, let users): + return ("stories", [("count", count as Any), ("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_stories(_ reader: BufferReader) -> Stories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.StoryItem]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.stories.Stories.stories(count: _1!, stories: _2!, chats: _3!, users: _4!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum StoryReactionsList: TypeConstructorDescription { + case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): + if boxed { + buffer.appendInt32(-1436583780) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset): + return ("storyReactionsList", [("flags", flags as Any), ("count", count as Any), ("reactions", reactions as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + } + } + + public static func parse_storyReactionsList(_ reader: BufferReader) -> StoryReactionsList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.StoryReaction]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryReaction.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _6: String? + if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) + } + else { + return nil + } + } + + } +} public extension Api.stories { enum StoryViews: TypeConstructorDescription { case storyViews(views: [Api.StoryViews], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index 6983f44182..5930acd62f 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -5103,6 +5103,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getDefaultTagReactions(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1107741656) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getDefaultTagReactions", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Reactions? in + let reader = BufferReader(buffer) + var result: Api.messages.Reactions? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Reactions + } + return result + }) + } +} public extension Api.functions.messages { static func getDhConfig(version: Int32, randomLength: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5678,6 +5693,22 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getOutboxReadDate(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1941176739) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getOutboxReadDate", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.OutboxReadDate? in + let reader = BufferReader(buffer) + var result: Api.OutboxReadDate? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.OutboxReadDate + } + return result + }) + } +} public extension Api.functions.messages { static func getPeerDialogs(peers: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5907,6 +5938,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getSavedReactionTags(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1981668047) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSavedReactionTags", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedReactionTags? in + let reader = BufferReader(buffer) + var result: Api.messages.SavedReactionTags? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SavedReactionTags + } + return result + }) + } +} public extension Api.functions.messages { static func getScheduledHistory(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -6785,14 +6831,19 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, savedPeerId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, savedPeerId: Api.InputPeer?, savedReaction: [Api.Reaction]?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1481316055) + buffer.appendInt32(703497338) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) serializeString(q, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {savedPeerId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(savedReaction!.count)) + for item in savedReaction! { + item.serialize(buffer, true) + }} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} filter.serialize(buffer, true) serializeInt32(minDate, buffer: buffer, boxed: false) @@ -6803,7 +6854,7 @@ public extension Api.functions.messages { serializeInt32(maxId, buffer: buffer, boxed: false) serializeInt32(minId, buffer: buffer, boxed: false) serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("savedPeerId", String(describing: savedPeerId)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("savedPeerId", String(describing: savedPeerId)), ("savedReaction", String(describing: savedReaction)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in let reader = BufferReader(buffer) var result: Api.messages.Messages? if let signature = reader.readInt32() { @@ -7709,6 +7760,23 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func updateSavedReactionTag(flags: Int32, reaction: Api.Reaction, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1613331948) + serializeInt32(flags, buffer: buffer, boxed: false) + reaction.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.updateSavedReactionTag", parameters: [("flags", String(describing: flags)), ("reaction", String(describing: reaction)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.messages { static func uploadEncryptedFile(peer: Api.InputEncryptedChat, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift index 2b341e349e..5a69a0a61a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift @@ -8,6 +8,7 @@ extension ReactionsMessageAttribute { case let .messageReactions(flags, results, recentReactions): let min = (flags & (1 << 0)) != 0 let canViewList = (flags & (1 << 2)) != 0 + let isTags = (flags & (1 << 3)) != 0 var reactions = results.compactMap { result -> MessageReaction? in switch result { case let .reactionCount(_, chosenOrder, reaction, count): @@ -53,7 +54,7 @@ extension ReactionsMessageAttribute { } } } - return ReactionsMessageAttribute(canViewList: canViewList, reactions: reactions, recentPeers: parsedRecentReactions) + return ReactionsMessageAttribute(canViewList: canViewList, isTags: isTags, reactions: reactions, recentPeers: parsedRecentReactions) } } } @@ -169,7 +170,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM recentPeers = updatedRecentPeers if !reactions.isEmpty { - return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, reactions: reactions, recentPeers: recentPeers) + return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, isTags: current?.isTags ?? false, reactions: reactions, recentPeers: recentPeers) } else { return nil } @@ -185,6 +186,7 @@ extension ReactionsMessageAttribute { switch apiReactions { case let .messageReactions(flags, results, recentReactions): let canViewList = (flags & (1 << 2)) != 0 + let isTags = (flags & (1 << 3)) != 0 let parsedRecentReactions: [ReactionsMessageAttribute.RecentPeer] if let recentReactions = recentReactions { parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in @@ -206,6 +208,7 @@ extension ReactionsMessageAttribute { self.init( canViewList: canViewList, + isTags: isTags, reactions: results.compactMap { result -> MessageReaction? in switch result { case let .reactionCount(_, chosenOrder, reaction, count): diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift index 8cf6224660..3df766544f 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift @@ -6,18 +6,20 @@ import TelegramApi extension TelegramUserPresence { convenience init(apiStatus: Api.UserStatus) { switch apiStatus { - case .userStatusEmpty: - self.init(status: .none, lastActivity: 0) - case let .userStatusOnline(expires): - self.init(status: .present(until: expires), lastActivity: 0) - case let .userStatusOffline(wasOnline): - self.init(status: .present(until: wasOnline), lastActivity: 0) - case .userStatusRecently: - self.init(status: .recently, lastActivity: 0) - case .userStatusLastWeek: - self.init(status: .lastWeek, lastActivity: 0) - case .userStatusLastMonth: - self.init(status: .lastMonth, lastActivity: 0) + case .userStatusEmpty: + self.init(status: .none, lastActivity: 0) + case .userStatusHidden: + self.init(status: .hidden, lastActivity: 0) + case let .userStatusOnline(expires): + self.init(status: .present(until: expires), lastActivity: 0) + case let .userStatusOffline(wasOnline): + self.init(status: .present(until: wasOnline), lastActivity: 0) + case .userStatusRecently: + self.init(status: .recently, lastActivity: 0) + case .userStatusLastWeek: + self.init(status: .lastWeek, lastActivity: 0) + case .userStatusLastMonth: + self.init(status: .lastMonth, lastActivity: 0) } } diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index bbd8f0c454..9ae1fa5e64 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -89,6 +89,7 @@ final class AccountTaskManager { tasks.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedApplyPendingScheduledMessagesActions(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start()) tasks.add(managedSynchronizeAvailableReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) + tasks.add(managedSynchronizeSavedMessageTags(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(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()) @@ -108,6 +109,7 @@ final class AccountTaskManager { tasks.add(managedGroupPhotoEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedBackgroundIconEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedRecentReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) + tasks.add(managedDefaultTagReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(_internal_loadedStickerPack(postbox: self.stateManager.postbox, network: self.stateManager.network, reference: .iconStatusEmoji, forceActualized: true).start()) tasks.add(_internal_loadedStickerPack(postbox: self.stateManager.postbox, network: self.stateManager.network, reference: .iconChannelStatusEmoji, forceActualized: true).start()) tasks.add(managedDisabledChannelStatusIconEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index 9a81013195..299e13bfa0 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -512,7 +512,7 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac } else if tag == MessageTags.unseenReaction { requestSignal = network.request(Api.functions.messages.getUnreadReactions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1)) } else if let filter = messageFilterForTagMask(tag) { - requestSignal = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) + requestSignal = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, savedReaction: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) } else { assertionFailure() requestSignal = .complete() @@ -582,7 +582,7 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network var flags: Int32 = 0 flags |= (1 << 1) - requestSignal = network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: threadMessageId, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) + requestSignal = network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, savedReaction: nil, topMsgId: threadMessageId, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) } else { return .complete() } diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index 7d4c93c6ea..e78bfe6d54 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -739,7 +739,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH } } - request = source.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: savedPeerId, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) + request = source.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: savedPeerId, savedReaction: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) } else { assertionFailure() minMaxRange = 1 ... 1 @@ -1043,7 +1043,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId offset = single((holeIndex.timestamp, min(holeIndex.id.id, Int32.max - 1) + 1, Api.InputPeer.inputPeerEmpty), NoError.self) return offset |> mapToSignal { (timestamp, id, peer) -> Signal in - let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0)) + let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, savedPeerId: nil, savedReaction: nil, topMsgId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0)) |> retryRequest |> mapToSignal { result -> Signal in let messages: [Api.Message] diff --git a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift index c2fa13e3c8..2c2129cdeb 100644 --- a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift +++ b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift @@ -160,7 +160,7 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId: if let _ = peer as? TelegramUser { if let presence = transaction.getPeerPresence(peerId: peerId) as? TelegramUserPresence { switch presence.status { - case .none, .lastWeek, .lastMonth: + case .none, .lastWeek, .lastMonth, .hidden: return .complete() case .recently: break diff --git a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift index 508a965546..4b73712944 100644 --- a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift +++ b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift @@ -505,3 +505,54 @@ func managedTopReactions(postbox: Postbox, network: Network) -> Signal then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart } + +func managedDefaultTagReactions(postbox: Postbox, network: Network) -> Signal { + let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudDefaultTagReactions, extractItemId: { rawId in + switch RecentReactionItemId(rawId).id { + case .builtin: + return 0 + case let .custom(fileId): + return fileId.id + } + }, reverseHashOrder: false, forceFetch: false, fetch: { hash in + return network.request(Api.functions.messages.getDefaultTagReactions(hash: hash)) + |> retryRequest + |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in + switch result { + case .reactionsNotModified: + return .single(nil) + case let .reactions(_, reactions): + let parsedReactions = reactions.compactMap(MessageReaction.Reaction.init(apiReaction:)) + + return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedReactions.compactMap { reaction -> Int64? in + switch reaction { + case .builtin: + return nil + case let .custom(fileId): + return fileId + } + }) + |> map { files -> [OrderedItemListEntry] in + var items: [OrderedItemListEntry] = [] + for reaction in parsedReactions { + let item: RecentReactionItem + switch reaction { + case let .builtin(value): + item = RecentReactionItem(.builtin(value)) + case let .custom(fileId): + guard let file = files[fileId] else { + continue + } + item = RecentReactionItem(.custom(file)) + } + if let entry = CodableEntry(item) { + items.append(OrderedItemListEntry(id: item.id.rawValue, contents: entry)) + } + } + return items + } + } + } + }) + return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart +} diff --git a/submodules/TelegramCore/Sources/State/SavedMessageTags.swift b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift new file mode 100644 index 0000000000..44207a6a23 --- /dev/null +++ b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift @@ -0,0 +1,196 @@ +import Foundation +import TelegramApi +import Postbox +import SwiftSignalKit + +public final class SavedMessageTags: Equatable, Codable { + public final class Tag: Equatable, Codable { + private enum CodingKeys: String, CodingKey { + case reaction + case title + case count + } + + public let reaction: MessageReaction.Reaction + public let title: String? + public let count: Int + + public init( + reaction: MessageReaction.Reaction, + title: String?, + count: Int + ) { + self.reaction = reaction + self.title = title + self.count = count + } + + public static func ==(lhs: Tag, rhs: Tag) -> Bool { + if lhs.reaction != rhs.reaction { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.count != rhs.count { + return false + } + return true + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.reaction = try container.decode(MessageReaction.Reaction.self, forKey: .reaction) + + self.title = try container.decodeIfPresent(String.self, forKey: .title) + self.count = Int(try container.decode(Int32.self, forKey: .count)) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.reaction, forKey: .reaction) + try container.encodeIfPresent(self.title, forKey: .title) + + try container.encode(Int32(self.count), forKey: .count) + } + } + + private enum CodingKeys: String, CodingKey { + case newHash + case tags + } + + public let hash: Int64 + public let tags: [Tag] + + public init( + hash: Int64, + tags: [Tag] + ) { + self.hash = hash + self.tags = tags + } + + public static func ==(lhs: SavedMessageTags, rhs: SavedMessageTags) -> Bool { + if lhs.hash != rhs.hash { + return false + } + if lhs.tags != rhs.tags { + return false + } + return true + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.hash = try container.decodeIfPresent(Int64.self, forKey: .newHash) ?? 0 + self.tags = try container.decode([Tag].self, forKey: .tags) + } + + 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.tags, forKey: .tags) + } +} + +func _internal_savedMessageTags(postbox: Postbox) -> Signal { + return postbox.transaction { transaction -> SavedMessageTags? in + return _internal_savedMessageTags(transaction: transaction) + } +} + +func _internal_savedMessageTags(transaction: Transaction) -> SavedMessageTags? { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) + + let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.savedMessageTags, key: key))?.get(SavedMessageTags.self) + if let cached = cached { + return cached + } else { + return nil + } +} + +func _internal_setSavedMessageTags(transaction: Transaction, savedMessageTags: SavedMessageTags) { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) + + if let entry = CodableEntry(savedMessageTags) { + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.savedMessageTags, key: key), entry: entry) + } +} + +func managedSynchronizeSavedMessageTags(postbox: Postbox, network: Network) -> Signal { + let poll = Signal { subscriber in + let signal: Signal = _internal_savedMessageTags(postbox: postbox) + |> mapToSignal { current in + return (network.request(Api.functions.messages.getSavedReactionTags(hash: current?.hash ?? 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .complete() + } + + switch result { + case .savedReactionTagsNotModified: + return .complete() + case let .savedReactionTags(tags, hash): + var customFileIds: [Int64] = [] + + var parsedTags: [SavedMessageTags.Tag] = [] + for tag in tags { + switch tag { + case let .savedReactionTag(_, reaction, title, count): + guard let reaction = MessageReaction.Reaction(apiReaction: reaction) else { + continue + } + parsedTags.append(SavedMessageTags.Tag( + reaction: reaction, + title: title, + count: Int(count) + )) + + if case let .custom(fileId) = reaction { + customFileIds.append(fileId) + } + } + } + + let savedMessageTags = SavedMessageTags( + hash: hash, + tags: parsedTags + ) + + return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: customFileIds) + |> mapToSignal { _ -> Signal in + return postbox.transaction { transaction in + _internal_setSavedMessageTags(transaction: transaction, savedMessageTags: savedMessageTags) + } + |> ignoreValues + } + } + }) + } + + return signal.start(completed: { + subscriber.putCompletion() + }) + } + + return ( + poll + |> then( + .complete() + |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()) + ) + ) + |> restart +} diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index d02ad2476c..3ef225aaab 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 170 + return 171 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index 140799e0b8..c8abd9c147 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -84,6 +84,7 @@ public struct Namespaces { public static let CloudFeaturedBackgroundIconEmoji: Int32 = 26 public static let CloudFeaturedChannelStatusEmoji: Int32 = 27 public static let CloudDisabledChannelStatusEmoji: Int32 = 28 + public static let CloudDefaultTagReactions: Int32 = 29 } public struct CachedItemCollection { @@ -118,6 +119,7 @@ public struct Namespaces { public static let displayedMessageNotifications: Int8 = 32 public static let recommendedChannels: Int8 = 33 public static let peerColorOptions: Int8 = 34 + public static let savedMessageTags: Int8 = 35 } public struct UnorderedItemList { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index 9368b3f577..437fdcd7d7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -195,6 +195,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { } public let canViewList: Bool + public let isTags: Bool public let reactions: [MessageReaction] public let recentPeers: [RecentPeer] @@ -220,20 +221,23 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { return result } - public init(canViewList: Bool, reactions: [MessageReaction], recentPeers: [RecentPeer]) { + public init(canViewList: Bool, isTags: Bool, reactions: [MessageReaction], recentPeers: [RecentPeer]) { self.canViewList = canViewList + self.isTags = isTags self.reactions = reactions self.recentPeers = recentPeers } required public init(decoder: PostboxDecoder) { self.canViewList = decoder.decodeBoolForKey("vl", orElse: true) + self.isTags = decoder.decodeBoolForKey("tg", orElse: false) self.reactions = decoder.decodeObjectArrayWithDecoderForKey("r") self.recentPeers = decoder.decodeObjectArrayWithDecoderForKey("rp") } public func encode(_ encoder: PostboxEncoder) { encoder.encodeBool(self.canViewList, forKey: "vl") + encoder.encodeBool(self.isTags, forKey: "tg") encoder.encodeObjectArray(self.reactions, forKey: "r") encoder.encodeObjectArray(self.recentPeers, forKey: "rp") } @@ -242,6 +246,9 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { if lhs.canViewList != rhs.canViewList { return false } + if lhs.isTags != rhs.isTags { + return false + } if lhs.reactions != rhs.reactions { return false } @@ -263,6 +270,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { public func withAllSeen() -> ReactionsMessageAttribute { return ReactionsMessageAttribute( canViewList: self.canViewList, + isTags: self.isTags, reactions: self.reactions, recentPeers: self.recentPeers.map { recentPeer in var recentPeer = recentPeer diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUserPresence.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUserPresence.swift index e220adde73..829dae85a7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUserPresence.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUserPresence.swift @@ -1,84 +1,85 @@ import Postbox public enum UserPresenceStatus: Comparable, PostboxCoding { + private struct SortKey: Comparable { + var major: Int + var minor: Int32 + + init(major: Int, minor: Int32) { + self.major = major + self.minor = minor + } + + static func <(lhs: SortKey, rhs: SortKey) -> Bool { + if lhs.major != rhs.major { + return lhs.major < rhs.major + } + return lhs.minor < rhs.minor + } + } + case none + case hidden case present(until: Int32) case recently case lastWeek case lastMonth - public static func <(lhs: UserPresenceStatus, rhs: UserPresenceStatus) -> Bool { - switch lhs { - case .none: - switch rhs { - case .none: - return false - case .lastMonth, .lastWeek, .recently, .present: - return true - } - case let .present(until): - switch rhs { - case .none: - return false - case let .present(rhsUntil): - return until < rhsUntil - case .lastWeek, .lastMonth, .recently: - return false - } - case .recently: - switch rhs { - case .none, .lastWeek, .lastMonth, .recently: - return false - case .present: - return true - } - case .lastWeek: - switch rhs { - case .none, .lastMonth, .lastWeek: - return false - case .present, .recently: - return true - } - case .lastMonth: - switch rhs { - case .none, .lastMonth: - return false - case .present, .recently, lastWeek: - return true - } + private var sortKey: SortKey { + switch self { + case let .present(until): + return SortKey(major: 1, minor: until) + case .hidden: + return SortKey(major: 2, minor: 0) + case .recently: + return SortKey(major: 3, minor: 0) + case .lastWeek: + return SortKey(major: 4, minor: 0) + case .lastMonth: + return SortKey(major: 5, minor: 0) + case .none: + return SortKey(major: 6, minor: 0) } } + public static func <(lhs: UserPresenceStatus, rhs: UserPresenceStatus) -> Bool { + return lhs.sortKey < rhs.sortKey + } + public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { - case 0: - self = .none - case 1: - self = .present(until: decoder.decodeInt32ForKey("t", orElse: 0)) - case 2: - self = .recently - case 3: - self = .lastWeek - case 4: - self = .lastMonth - default: - self = .none + case 0: + self = .none + case 1: + self = .present(until: decoder.decodeInt32ForKey("t", orElse: 0)) + case 2: + self = .recently + case 3: + self = .lastWeek + case 4: + self = .lastMonth + case 5: + self = .hidden + default: + self = .none } } public func encode(_ encoder: PostboxEncoder) { switch self { - case .none: - encoder.encodeInt32(0, forKey: "v") - case let .present(timestamp): - encoder.encodeInt32(1, forKey: "v") - encoder.encodeInt32(timestamp, forKey: "t") - case .recently: - encoder.encodeInt32(2, forKey: "v") - case .lastWeek: - encoder.encodeInt32(3, forKey: "v") - case .lastMonth: - encoder.encodeInt32(4, forKey: "v") + case .none: + encoder.encodeInt32(0, forKey: "v") + case let .present(timestamp): + encoder.encodeInt32(1, forKey: "v") + encoder.encodeInt32(timestamp, forKey: "t") + case .recently: + encoder.encodeInt32(2, forKey: "v") + case .lastWeek: + encoder.encodeInt32(3, forKey: "v") + case .lastMonth: + encoder.encodeInt32(4, forKey: "v") + case .hidden: + encoder.encodeInt32(5, forKey: "v") } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index 7b8ad48961..9ed290d7a3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -8,7 +8,7 @@ import MtProtoKit public enum SearchMessagesLocation: Equatable { case general(tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) - case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, threadId: Int64?, minDate: Int32?, maxDate: Int32?) + case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, reactions: [MessageReaction.Reaction]?, threadId: Int64?, minDate: Int32?, maxDate: Int32?) case sentMedia(tags: MessageTags?) } @@ -224,7 +224,7 @@ private func mergedResult(_ state: SearchMessagesState) -> SearchMessagesResult func _internal_searchMessages(account: Account, location: SearchMessagesLocation, query: String, state: SearchMessagesState?, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> { let remoteSearchResult: Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> switch location { - case let .peer(peerId, fromId, tags, threadId, minDate, maxDate): + case let .peer(peerId, fromId, tags, reactions, threadId, minDate, maxDate): if peerId.namespace == Namespaces.Peer.SecretChat { return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in var readStates: [PeerId: CombinedPeerReadState] = [:] @@ -297,6 +297,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation flags |= (1 << 1) topMsgId = Int32(clamping: threadId) } + let peerMessages: Signal if let completed = state?.main.completed, completed { peerMessages = .single(nil) @@ -306,7 +307,18 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && fromId == nil && tags == nil && minDate == nil && maxDate == nil { signal = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: lowerBound?.id.id ?? 0, offsetDate: 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) } else { - signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + var savedReactions: [Api.Reaction]? + if let reactions = reactions { + savedReactions = reactions.map { + $0.apiReaction + } + } + + if savedReactions != nil { + flags |= 1 << 3 + } + + signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, savedReaction: savedReactions, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) } peerMessages = signal |> map(Optional.init) @@ -322,7 +334,19 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation additionalPeerMessages = .single(nil) } else if mainCompleted || !hasAdditional { let lowerBound = state?.additional?.messages.last.flatMap({ $0.index }) - additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + + var savedReactions: [Api.Reaction]? + if let reactions = reactions { + savedReactions = reactions.map { + $0.apiReaction + } + } + + if savedReactions != nil { + flags |= 1 << 3 + } + + additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, savedReaction: savedReactions, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 393f752d4c..f90a55d121 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -448,7 +448,7 @@ public extension TelegramEngine { } } - signals.append(self.account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0)) + signals.append(self.account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: inputSavedPeer, savedReaction: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0)) |> map { result -> (count: Int32?, topId: Int32?) in switch result { case let .messagesSlice(_, count, _, _, messages, _, _): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index 06cbf99f62..2852acb77c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -6,52 +6,49 @@ public enum EnginePeer: Equatable { public struct Presence: Equatable { public enum Status: Comparable { + private struct SortKey: Comparable { + var major: Int + var minor: Int32 + + init(major: Int, minor: Int32) { + self.major = major + self.minor = minor + } + + static func <(lhs: SortKey, rhs: SortKey) -> Bool { + if lhs.major != rhs.major { + return lhs.major < rhs.major + } + return lhs.minor < rhs.minor + } + } + case present(until: Int32) + case hidden case recently case lastWeek case lastMonth case longTimeAgo + + private var sortKey: SortKey { + switch self { + case let .present(until): + return SortKey(major: 1, minor: until) + case .hidden: + return SortKey(major: 2, minor: 0) + case .recently: + return SortKey(major: 3, minor: 0) + case .lastWeek: + return SortKey(major: 4, minor: 0) + case .lastMonth: + return SortKey(major: 5, minor: 0) + case .longTimeAgo: + return SortKey(major: 6, minor: 0) + } + } public static func <(lhs: Status, rhs: Status) -> Bool { - switch lhs { - case .longTimeAgo: - switch rhs { - case .longTimeAgo: - return false - case .lastMonth, .lastWeek, .recently, .present: - return true - } - case let .present(until): - switch rhs { - case .longTimeAgo: - return false - case let .present(rhsUntil): - return until < rhsUntil - case .lastWeek, .lastMonth, .recently: - return false - } - case .recently: - switch rhs { - case .longTimeAgo, .lastWeek, .lastMonth, .recently: - return false - case .present: - return true - } - case .lastWeek: - switch rhs { - case .longTimeAgo, .lastMonth, .lastWeek: - return false - case .present, .recently: - return true - } - case .lastMonth: - switch rhs { - case .longTimeAgo, .lastMonth: - return false - case .present, .recently, lastWeek: - return true - } - } + return lhs.sortKey < rhs.sortKey } } @@ -368,6 +365,8 @@ public extension EnginePeer.Presence { switch presence.status { case .none: mappedStatus = .longTimeAgo + case .hidden: + mappedStatus = .hidden case let .present(until): mappedStatus = .present(until: until) case .recently: @@ -387,6 +386,8 @@ public extension EnginePeer.Presence { func _asPresence() -> TelegramUserPresence { let mappedStatus: UserPresenceStatus switch self.status { + case .hidden: + mappedStatus = .hidden case .longTimeAgo: mappedStatus = .none case let .present(until): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift index e96e1ee6ba..0dce03b35c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift @@ -68,7 +68,7 @@ func _internal_requestPeerPhotos(accountPeerId: PeerId, postbox: Postbox, networ } } } else if let peer = peer, let inputPeer = apiInputPeer(peer) { - return network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) + return network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, savedReaction: nil, topMsgId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 54aca5647e..8374142db5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -109,6 +109,24 @@ public extension TelegramEngine { return _internal_cachedAvailableReactions(postbox: self.account.postbox) } + public func savedMessageTags() -> Signal<([SavedMessageTags.Tag], [Int64: TelegramMediaFile]), NoError> { + return self.account.postbox.transaction { transaction -> ([SavedMessageTags.Tag], [Int64: TelegramMediaFile]) in + guard let savedMessageTags = _internal_savedMessageTags(transaction: transaction) else { + return ([], [:]) + } + var files: [Int64: TelegramMediaFile] = [:] + for tag in savedMessageTags.tags { + if case let .custom(fileId) = tag.reaction { + let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) + if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { + files[fileId] = file + } + } + } + return (savedMessageTags.tags, files) + } + } + public func emojiSearchCategories(kind: EmojiSearchCategories.Kind) -> Signal { return _internal_cachedEmojiSearchCategories(postbox: self.account.postbox, kind: kind) } diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 84a3f55f96..4048243992 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -417,6 +417,17 @@ public extension Message { } return nil } + var effectiveReactionsAttribute: ReactionsMessageAttribute? { + if !self.hasReactions { + return nil + } + + if let result = mergedMessageReactions(attributes: self.attributes) { + return result + } else { + return nil + } + } var effectiveReactions: [MessageReaction]? { if !self.hasReactions { return nil @@ -476,6 +487,19 @@ public extension Message { } } +public extension Message { + func areReactionsTags(accountPeerId: PeerId) -> Bool { + if self.id.peerId == accountPeerId { + if let reactionsAttribute = self.reactionsAttribute, !reactionsAttribute.reactions.isEmpty { + return reactionsAttribute.isTags + } else { + return true + } + } + return false + } +} + public func _internal_parseMediaAttachment(data: Data) -> Media? { guard let object = Api.parse(Buffer(buffer: MemoryBuffer(data: data))) else { return nil diff --git a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift index b8f5bbd3db..a365a84def 100644 --- a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift @@ -216,6 +216,7 @@ public enum RelativeUserPresenceStatus { case offline case online(at: Int32) case lastSeen(at: Int32) + case hidden case recently case lastWeek case lastMonth @@ -242,6 +243,8 @@ public func relativeUserPresenceStatus(_ presence: EnginePeer.Presence, relative return .lastWeek case .lastMonth: return .lastMonth + case .hidden: + return .hidden } } @@ -537,6 +540,9 @@ public func stringAndActivityForUserPresence(strings: PresentationStrings, dateT return (strings.LastSeen_WithinAMonth, false) case .longTimeAgo: return (strings.LastSeen_ALongTimeAgo, false) + case .hidden: + //TODO:localize + return ("last seen hidden", false) } } @@ -586,7 +592,7 @@ public func userPresenceStringRefreshTimeout(_ presence: TelegramUserPresence, r } else { return Double.infinity } - case .none, .lastWeek, .lastMonth: + case .none, .lastWeek, .lastMonth, .hidden: return Double.infinity } } diff --git a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift index a6f74c60e9..32331874e9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift @@ -196,7 +196,7 @@ public final class ChatHistorySearchContainerNode: SearchDisplayControllerConten if let strongSelf = self { let signal: Signal<([ChatHistorySearchEntry], [MessageId: Message])?, NoError> if let query = query, !query.isEmpty { - let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: threadId, minDate: nil, maxDate: nil), query: query, state: nil) + let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: threadId, minDate: nil, maxDate: nil), query: query, state: nil) |> map { $0.0.messages } |> delay(0.2, queue: Queue.concurrentDefaultQueue()) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 2a3178bbd8..a73458f7c1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1045,7 +1045,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -1241,9 +1241,9 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { - reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? if !reactions.reactions.isEmpty { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 541b9742a9..e33cf03c17 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -657,7 +657,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: chatLocation == .peer(id: context.account.peerId), + areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), replyCount: dateReplies, isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 51edc4886e..9957cb5ae9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1894,9 +1894,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let bubbleReactions: ReactionsMessageAttribute if needReactions { - bubbleReactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + bubbleReactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - bubbleReactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + bubbleReactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } if !bubbleReactions.reactions.isEmpty && !item.presentationData.isPreview { bottomNodeMergeStatus = .Both @@ -2113,7 +2113,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index 139a689678..5221c3a7af 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -238,7 +238,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 824cc1772d..33e80d1fca 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -228,7 +228,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var reactions: [MessageReaction] var reactionPeers: [(MessageReaction.Reaction, EnginePeer)] var displayAllReactionPeers: Bool - var isSavedMessages: Bool + var areReactionsTags: Bool var replyCount: Int var isPinned: Bool var hasAutoremove: Bool @@ -249,7 +249,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { reactions: [MessageReaction], reactionPeers: [(MessageReaction.Reaction, EnginePeer)], displayAllReactionPeers: Bool, - isSavedMessages: Bool, + areReactionsTags: Bool, replyCount: Int, isPinned: Bool, hasAutoremove: Bool, @@ -269,7 +269,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { self.reactions = reactions self.reactionPeers = reactionPeers self.displayAllReactionPeers = displayAllReactionPeers - self.isSavedMessages = isSavedMessages + self.areReactionsTags = areReactionsTags self.replyCount = replyCount self.isPinned = isPinned self.hasAutoremove = hasAutoremove @@ -747,7 +747,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { }, reactions: [], colors: reactionColors, - isTag: arguments.isSavedMessages, + isTag: arguments.areReactionsTags, constrainedWidth: arguments.constrainedSize.width ) case let .trailingContent(contentWidth, reactionSettings): @@ -809,7 +809,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { ) }, colors: reactionColors, - isTag: arguments.isSavedMessages, + isTag: arguments.areReactionsTags, constrainedWidth: arguments.constrainedSize.width ) } else { @@ -823,7 +823,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { }, reactions: [], colors: reactionColors, - isTag: arguments.isSavedMessages, + isTag: arguments.areReactionsTags, constrainedWidth: arguments.constrainedSize.width ) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index a206105c42..e84182d137 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -519,7 +519,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index 62303af69c..53b1bc1738 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -595,9 +595,9 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { - reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index 0aca23b715..abc9fe8f2e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -937,7 +937,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: arguments.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: arguments.chatLocation == .peer(id: arguments.context.account.peerId), + areReactionsTags: arguments.message.areReactionsTags(accountPeerId: arguments.context.account.peerId), 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 6144637ec1..8bb43fc111 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -570,7 +570,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), 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 5ac8ce5904..7560a4940c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -872,7 +872,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr reactions: dateAndStatus.dateReactions, reactionPeers: dateAndStatus.dateReactionPeers, displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: message.id.peerId == context.account.peerId, + areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId), replyCount: dateAndStatus.dateReplies, isPinned: dateAndStatus.isPinned, hasAutoremove: message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index f0c7e86898..6ceb6ca323 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -276,7 +276,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index 6318eb0633..ee208d3bc7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1024,7 +1024,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift index ae9356212d..38f20827d0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift @@ -178,7 +178,7 @@ public final class MessageReactionButtonsNode: ASDisplayNode { ) }, colors: reactionColors, - isTag: message.id.peerId == context.account.peerId, + isTag: message.areReactionsTags(accountPeerId: context.account.peerId), constrainedWidth: constrainedWidth ) @@ -498,7 +498,7 @@ public final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleConte } return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in - let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) let buttonsUpdate = buttonsNode.prepareUpdate( context: item.context, presentationData: item.presentationData, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift index 4ba96ab7cc..62a1f237de 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -132,7 +132,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), 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 22d2c1b2c0..b409e8ae20 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -628,7 +628,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, @@ -823,9 +823,9 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { - reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 0d47147dc2..bd06bf9063 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -573,7 +573,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { reactions: dateReactionsAndPeers.reactions, reactionPeers: dateReactionsAndPeers.peers, displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, - isSavedMessages: item.chatLocation.peerId == item.context.account.peerId, + areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId), replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread), hasAutoremove: item.message.isSelfExpiring, diff --git a/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal b/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal index 0f9f8fee11..f2b8683051 100644 --- a/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal +++ b/submodules/TelegramUI/Components/DustEffect/Metal/DustEffectShaders.metal @@ -74,6 +74,34 @@ float particleEaseInValueAt(float fraction, float t) { return localT; } +float2 grad(float2 z ) { + // 2D to 1D (feel free to replace by some other) + int n = z.x + z.y * 11111.0; + + // Hugo Elias hash (feel free to replace by another one) + n = (n << 13) ^ n; + n = (n * (n * n * 15731 + 789221) + 1376312589) >> 16; + + // Perlin style vectors + n &= 7; + float2 gr = float2(n & 1, n >> 1) * 2.0 - 1.0; + return ( n>=6 ) ? float2(0.0, gr.x) : + ( n>=4 ) ? float2(gr.x, 0.0) : + gr; +} + +float noise(float2 p ) { + float2 i = float2(floor(p)); + float2 f = fract(p); + + float2 u = f*f*(3.0-2.0*f); // feel free to replace by a quintic smoothstep instead + + return mix( mix( dot( grad( i+float2(0,0) ), f-float2(0.0,0.0) ), + dot( grad( i+float2(1,0) ), f-float2(1.0,0.0) ), u.x), + mix( dot( grad( i+float2(0,1) ), f-float2(0.0,1.0) ), + dot( grad( i+float2(1,1) ), f-float2(1.0,1.0) ), u.x), u.y); +} + kernel void dustEffectUpdateParticle( device Particle *particles [[ buffer(0) ]], const device uint2 &size [[ buffer(1) ]], @@ -139,5 +167,6 @@ fragment half4 dustEffectFragment( constexpr sampler sampler(coord::normalized, address::clamp_to_edge, filter::linear); half4 color = inTexture.sample(sampler, float2(in.uv.x, 1.0 - in.uv.y)); + return color * in.alpha; } diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index e204821d54..a44750a2d8 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -59,6 +59,7 @@ public final class EmojiStatusComponent: Component { public let animationCache: AnimationCache public let animationRenderer: MultiAnimationRenderer public let content: Content + public let size: CGSize? public let isVisibleForAnimations: Bool public let useSharedAnimation: Bool public let action: (() -> Void)? @@ -69,6 +70,7 @@ public final class EmojiStatusComponent: Component { animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, content: Content, + size: CGSize? = nil, isVisibleForAnimations: Bool, useSharedAnimation: Bool = false, action: (() -> Void)?, @@ -83,6 +85,7 @@ public final class EmojiStatusComponent: Component { animationCache: animationCache, animationRenderer: animationRenderer, content: content, + size: size, isVisibleForAnimations: isVisibleForAnimations, useSharedAnimation: useSharedAnimation, action: action, @@ -97,6 +100,7 @@ public final class EmojiStatusComponent: Component { animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, content: Content, + size: CGSize? = nil, isVisibleForAnimations: Bool, useSharedAnimation: Bool = false, action: (() -> Void)?, @@ -108,6 +112,7 @@ public final class EmojiStatusComponent: Component { self.animationCache = animationCache self.animationRenderer = animationRenderer self.content = content + self.size = size self.isVisibleForAnimations = isVisibleForAnimations self.useSharedAnimation = useSharedAnimation self.action = action @@ -122,6 +127,7 @@ public final class EmojiStatusComponent: Component { animationCache: self.animationCache, animationRenderer: self.animationRenderer, content: self.content, + size: self.size, isVisibleForAnimations: isVisibleForAnimations, useSharedAnimation: self.useSharedAnimation, action: self.action, @@ -145,6 +151,9 @@ public final class EmojiStatusComponent: Component { if lhs.content != rhs.content { return false } + if lhs.size != rhs.size { + return false + } if lhs.isVisibleForAnimations != rhs.isVisibleForAnimations { return false } @@ -231,6 +240,8 @@ public final class EmojiStatusComponent: Component { } func update(component: EmojiStatusComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let availableSize = component.size ?? availableSize + self.state = state var iconImage: UIImage? diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 28589ba77a..257035d561 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -7167,1876 +7167,6 @@ public final class EmojiPagerContentComponent: Component { public 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) } - - private static func hasPremium(context: AccountContext, chatPeerId: EnginePeer.Id?, premiumIfSavedMessages: Bool) -> Signal { - let hasPremium: Signal - if premiumIfSavedMessages, let chatPeerId = chatPeerId, chatPeerId == context.account.peerId { - hasPremium = .single(true) - } else { - hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) - |> map { peer -> Bool in - guard case let .user(user) = peer else { - return false - } - return user.isPremium - } - |> distinctUntilChanged - } - return hasPremium - } - - public enum Subject: Equatable { - case generic - case status - case channelStatus - case reaction(onlyTop: Bool) - case emoji - case topicIcon - case quickReaction - case profilePhoto - case groupPhoto - case backgroundIcon - case reactionList - } - - public static func emojiInputData( - context: AccountContext, - animationCache: AnimationCache, - animationRenderer: MultiAnimationRenderer, - isStandalone: Bool, - subject: Subject, - hasTrending: Bool, - topReactionItems: [EmojiComponentReactionItem], - areUnicodeEmojiEnabled: Bool, - areCustomEmojiEnabled: Bool, - chatPeerId: EnginePeer.Id?, - selectedItems: Set = Set(), - topStatusTitle: String? = nil, - topicTitle: String? = nil, - topicColor: Int32? = nil, - backgroundIconColor: UIColor? = nil, - hasSearch: Bool = true, - forceHasPremium: Bool = false, - premiumIfSavedMessages: Bool = true, - 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 - - var orderedItemListCollectionIds: [Int32] = [] - - switch subject { - case .backgroundIcon, .reactionList: - break - default: - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.LocalRecentEmoji) - } - - var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([]) - - if case .status = subject { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji) - - iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) - |> map { result -> [TelegramMediaFile] in - switch result { - case let .result(_, items, _): - return items.map(\.file) - default: - return [] - } - } - |> take(1) - } else if case .channelStatus = subject { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedChannelStatusEmoji) - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudDisabledChannelStatusEmoji) - - iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconChannelStatusEmoji, forceActualized: false) - |> map { result -> [TelegramMediaFile] in - switch result { - case let .result(_, items, _): - return items.map(\.file) - default: - return [] - } - } - |> take(1) - } else if [.reaction(onlyTop: false), .quickReaction].contains(subject) { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions) - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions) - } else if case .topicIcon = subject { - iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false) - |> map { result -> [TelegramMediaFile] in - switch result { - case let .result(_, items, _): - return items.map(\.file) - default: - return [] - } - } - |> take(1) - } else if case .profilePhoto = subject { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji) - } else if case .groupPhoto = subject { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji) - } else if case .backgroundIcon = subject { - orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji) - } - - let availableReactions: Signal - if [.reaction(onlyTop: false), .quickReaction, .reactionList].contains(subject) { - availableReactions = context.engine.stickers.availableReactions() - } else { - availableReactions = .single(nil) - } - - let searchCategories: Signal - if [.emoji, .reaction(onlyTop: false), .reactionList].contains(subject) { - searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) - } else if case .status = subject { - searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) - } else if case .channelStatus = subject { - searchCategories = .single(nil) - } else if [.profilePhoto, .groupPhoto].contains(subject) { - searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) - } else { - searchCategories = .single(nil) - } - - let emojiItems: Signal = combineLatest( - context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: orderedItemListCollectionIds, namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), - forceHasPremium ? .single(true) : hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: premiumIfSavedMessages), - context.account.viewTracker.featuredEmojiPacks(), - availableReactions, - searchCategories, - iconStatusEmoji, - ApplicationSpecificNotice.dismissedTrendingEmojiPacks(accountManager: context.sharedContext.accountManager) - ) - |> map { view, hasPremium, featuredEmojiPacks, availableReactions, searchCategories, iconStatusEmoji, dismissedTrendingEmojiPacks -> EmojiPagerContentComponent in - struct ItemGroup { - var supergroupId: AnyHashable - var id: AnyHashable - var title: String? - var subtitle: String? - var isPremiumLocked: Bool - var isFeatured: Bool - var collapsedLineCount: Int? - var isClearable: Bool - var headerItem: EntityKeyboardAnimationData? - var items: [EmojiPagerContentComponent.Item] - } - var itemGroups: [ItemGroup] = [] - var itemGroupIndexById: [AnyHashable: Int] = [:] - - let maybeAppendUnicodeEmoji = { - let groupId: AnyHashable = "static" - - if itemGroupIndexById[groupId] != nil { - return - } - - if areUnicodeEmojiEnabled { - for (subgroupId, list) in staticEmojiMapping { - for emojiString in list { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .staticEmoji(emojiString), - itemFile: nil, - subgroupId: subgroupId.rawValue, - icon: .none, - tintMode: .none - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } - } - } - - var installedCollectionIds = Set() - for (id, _, _) in view.collectionInfos { - installedCollectionIds.insert(id) - } - - let dismissedTrendingEmojiPacksSet = Set(dismissedTrendingEmojiPacks ?? []) - let featuredEmojiPacksSet = Set(featuredEmojiPacks.map(\.info.id.id)) - - if dismissedTrendingEmojiPacksSet != featuredEmojiPacksSet && hasTrending { - for featuredEmojiPack in featuredEmojiPacks { - if installedCollectionIds.contains(featuredEmojiPack.info.id) { - continue - } - - guard let item = featuredEmojiPack.topItems.first else { - continue - } - - let animationData: EntityKeyboardAnimationData - - if let thumbnail = featuredEmojiPack.info.thumbnail { - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - animationData = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(featuredEmojiPack.info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: featuredEmojiPack.info.id.id, accessHash: featuredEmojiPack.info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: featuredEmojiPack.info.immediateThumbnailData, - isReaction: false, - isTemplate: false - ) - } else { - animationData = EntityKeyboardAnimationData(file: item.file) - } - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let supergroupId = "featuredTop" - let groupId: AnyHashable = supergroupId - let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium - if isPremiumLocked && isPremiumDisabled { - continue - } - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - let title = context.sharedContext.currentPresentationData.with({ $0 }).strings.EmojiInput_TrendingEmoji - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 0, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } - - var recentEmoji: OrderedItemListView? - var featuredStatusEmoji: OrderedItemListView? - var featuredChannelStatusEmoji: OrderedItemListView? - var disabledChannelStatusEmoji: OrderedItemListView? - var recentStatusEmoji: OrderedItemListView? - var topReactions: OrderedItemListView? - var recentReactions: OrderedItemListView? - var featuredAvatarEmoji: OrderedItemListView? - var featuredBackgroundIconEmoji: OrderedItemListView? - for orderedView in view.orderedItemListsViews { - if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji { - recentEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedStatusEmoji { - featuredStatusEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedChannelStatusEmoji { - featuredChannelStatusEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudDisabledChannelStatusEmoji { - disabledChannelStatusEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentStatusEmoji { - recentStatusEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentReactions { - recentReactions = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudTopReactions { - topReactions = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji { - featuredAvatarEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji { - featuredAvatarEmoji = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji { - featuredBackgroundIconEmoji = orderedView - } - } - - if case .topicIcon = subject { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .icon(.topic(String((topicTitle ?? "").prefix(1)), topicColor ?? 0)), - itemFile: nil, - subgroupId: nil, - icon: .none, - tintMode: .none - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) - } - - var existingIds = Set() - - for file in iconStatusEmoji { - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let resultItem: EmojiPagerContentComponent.Item - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } - } - } else if case .status = subject { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .icon(.premiumStar), - itemFile: nil, - subgroupId: nil, - icon: .none, - tintMode: .none - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) - } - - var existingIds = Set() - - for file in iconStatusEmoji.prefix(7) { - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let resultItem: EmojiPagerContentComponent.Item - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } - } - - if let recentStatusEmoji = recentStatusEmoji { - for item in recentStatusEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - - let file = item.media - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let resultItem: EmojiPagerContentComponent.Item - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { - break - } - - itemGroups[groupIndex].items.append(resultItem) - } - } - } - if let featuredStatusEmoji = featuredStatusEmoji { - for item in featuredStatusEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - - let file = item.media - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - let resultItem: EmojiPagerContentComponent.Item - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { - break - } - - itemGroups[groupIndex].items.append(resultItem) - } - } - } - } else if case .channelStatus = subject { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .icon(.stop), - itemFile: nil, - subgroupId: nil, - icon: .none, - tintMode: .accent - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) - } - - var existingIds = Set() - - if let disabledChannelStatusEmoji { - for item in disabledChannelStatusEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - let file = item.media - existingIds.insert(file.fileId) - } - } - - for file in iconStatusEmoji { - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let resultItem: EmojiPagerContentComponent.Item - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } - } - - if let featuredChannelStatusEmoji { - for item in featuredChannelStatusEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - - let file = item.media - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - let resultItem: EmojiPagerContentComponent.Item - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - /*if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { - break - }*/ - - itemGroups[groupIndex].items.append(resultItem) - } - } - } - } else if subject == .reactionList { - var existingIds = Set() - - if let availableReactions = availableReactions { - for reactionItem in availableReactions.reactions { - if !reactionItem.isEnabled { - continue - } - if existingIds.contains(reactionItem.value) { - continue - } - existingIds.insert(reactionItem.value) - - let icon: EmojiPagerContentComponent.Item.Icon - if !hasPremium, case .custom = reactionItem.value { - icon = .locked - } else { - icon = .none - } - - var tintMode: Item.TintMode = .none - if reactionItem.selectAnimation.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationFile = reactionItem.selectAnimation - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - let groupId = "liked" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } - } else if [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction].contains(subject) { - var existingIds = Set() - - var topReactionItems = topReactionItems - if topReactionItems.isEmpty { - if let topReactions = topReactions { - for item in topReactions.items { - guard let topReaction = item.contents.get(RecentReactionItem.self) else { - continue - } - - switch topReaction.content { - case let .builtin(value): - if let reaction = availableReactions?.reactions.first(where: { $0.value == .builtin(value) }) { - topReactionItems.append(EmojiComponentReactionItem(reaction: .builtin(value), file: reaction.selectAnimation)) - } else { - continue - } - case let .custom(file): - topReactionItems.append(EmojiComponentReactionItem(reaction: .custom(file.fileId.id), file: file)) - } - } - } - } - - let maxTopLineCount: Int - if case .reaction(onlyTop: true) = subject { - maxTopLineCount = 1000 - } else if hasPremium { - maxTopLineCount = 2 - } else { - maxTopLineCount = 6 - } - - for reactionItem in topReactionItems { - if existingIds.contains(reactionItem.reaction) { - continue - } - existingIds.insert(reactionItem.reaction) - - let icon: EmojiPagerContentComponent.Item.Icon - if case .reaction(onlyTop: true) = subject { - icon = .none - } else if !hasPremium, case .custom = reactionItem.reaction { - icon = .locked - } else { - icon = .none - } - - var tintMode: Item.TintMode = .none - if reactionItem.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationFile = reactionItem.file - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - - if itemGroups[groupIndex].items.count >= 8 * maxTopLineCount { - break - } - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - - if case .reaction(onlyTop: false) = subject { - var hasRecent = false - if let recentReactions = recentReactions, !recentReactions.items.isEmpty { - hasRecent = true - } - - let maxRecentLineCount: Int - if hasPremium { - maxRecentLineCount = 10 - } else { - maxRecentLineCount = 10 - } - - let popularTitle = hasRecent ? strings.Chat_ReactionSection_Recent : strings.Chat_ReactionSection_Popular - - if let availableReactions = availableReactions { - for reactionItem in availableReactions.reactions { - if !reactionItem.isEnabled { - continue - } - if existingIds.contains(reactionItem.value) { - continue - } - existingIds.insert(reactionItem.value) - - let icon: EmojiPagerContentComponent.Item.Icon - if !hasPremium, case .custom = reactionItem.value { - icon = .locked - } else { - icon = .none - } - - var tintMode: Item.TintMode = .none - if reactionItem.selectAnimation.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationFile = reactionItem.selectAnimation - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - if hasPremium { - let groupId = "popular" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) - } - } else { - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - - if itemGroups[groupIndex].items.count >= maxRecentLineCount * 8 { - break - } - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } - } - - if let recentReactions = recentReactions { - var popularInsertIndex = 0 - for item in recentReactions.items { - guard let item = item.contents.get(RecentReactionItem.self) else { - continue - } - - let animationFile: TelegramMediaFile - let icon: EmojiPagerContentComponent.Item.Icon - - switch item.content { - case let .builtin(value): - if existingIds.contains(.builtin(value)) { - continue - } - existingIds.insert(.builtin(value)) - if let availableReactions = availableReactions, let availableReaction = availableReactions.reactions.first(where: { $0.value == .builtin(value) }) { - if let centerAnimation = availableReaction.centerAnimation { - animationFile = centerAnimation - } else { - continue - } - } else { - continue - } - - icon = .none - case let .custom(file): - if existingIds.contains(.custom(file.fileId.id)) { - continue - } - existingIds.insert(.custom(file.fileId.id)) - animationFile = file - - if !hasPremium { - icon = .locked - } else { - icon = .none - } - } - - var tintMode: Item.TintMode = .none - if animationFile.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: animationFile, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - let groupId = "popular" - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count + 1 >= maxRecentLineCount * 8 { - break - } - - itemGroups[groupIndex].items.insert(resultItem, at: popularInsertIndex) - popularInsertIndex += 1 - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) - } - } - } - } - } else if [.profilePhoto, .groupPhoto].contains(subject) { - var existingIds = Set() - - let groupId = "recent" - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [])) - - if let featuredAvatarEmoji = featuredAvatarEmoji { - for item in featuredAvatarEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - - let file = item.media - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - let resultItem: EmojiPagerContentComponent.Item - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .accent - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { - break - } - - itemGroups[groupIndex].items.append(resultItem) - } - } - } - } else if case .backgroundIcon = subject { - var existingIds = Set() - - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .icon(.stop), - itemFile: nil, - subgroupId: nil, - icon: .none, - tintMode: .accent - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) - } - - if let featuredBackgroundIconEmoji { - for item in featuredBackgroundIconEmoji.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - - let file = item.media - if existingIds.contains(file.fileId) { - continue - } - existingIds.insert(file.fileId) - - let resultItem: EmojiPagerContentComponent.Item - - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - if let backgroundIconColor { - tintMode = .custom(backgroundIconColor) - } else { - tintMode = .accent - } - } - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute { - switch packReference { - case let .id(id, _): - if id == 773947703670341676 || id == 2964141614563343 { - tintMode = .accent - } - default: - break - } - } - } - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - if let groupIndex = itemGroupIndexById[groupId] { - if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { - break - } - - itemGroups[groupIndex].items.append(resultItem) - } - } - } - } - - let hasRecentEmoji = ![.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon, .reactionList].contains(subject) - - if let recentEmoji = recentEmoji, hasRecentEmoji { - for item in recentEmoji.items { - guard let item = item.contents.get(RecentEmojiItem.self) else { - continue - } - - if case let .file(file) = item.content, isPremiumDisabled, file.isPremiumEmoji { - continue - } - - if !areCustomEmojiEnabled, case .file = item.content { - continue - } - - let resultItem: EmojiPagerContentComponent.Item - switch item.content { - case let .file(file): - var tintMode: Item.TintMode = .none - if file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: file) - resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - case let .text(text): - resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .staticEmoji(text), - itemFile: nil, - subgroupId: nil, - icon: .none, - tintMode: .none - ) - } - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.Emoji_FrequentlyUsed, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: true, headerItem: nil, items: [resultItem])) - } - } - } - - if !hasPremium { - maybeAppendUnicodeEmoji() - } - - var itemCollectionMapping: [ItemCollectionId: StickerPackCollectionInfo] = [:] - for (id, info, _) in view.collectionInfos { - if let info = info as? StickerPackCollectionInfo { - itemCollectionMapping[id] = info - } - } - - var skippedCollectionIds = Set() - if areCustomEmojiEnabled { - for entry in view.entries { - guard let item = entry.item as? StickerPackItem else { - continue - } - - var icon: EmojiPagerContentComponent.Item.Icon = .none - if [.reaction(onlyTop: false), .quickReaction].contains(subject), !hasPremium { - icon = .locked - } - - let supergroupId = entry.index.collectionId - let groupId: AnyHashable = supergroupId - - if skippedCollectionIds.contains(groupId) { - continue - } - - if case .channelStatus = subject { - guard let collection = itemCollectionMapping[entry.index.collectionId] else { - continue - } - if !collection.flags.contains(.isAvailableAsChannelStatus) { - continue - } - } - - var isTemplate = false - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - if [.status, .channelStatus, .backgroundIcon].contains(subject) { - if let backgroundIconColor { - tintMode = .custom(backgroundIconColor) - } else { - tintMode = .accent - } - } else { - tintMode = .primary - } - if case .backgroundIcon = subject { - isTemplate = true - } - } else if case .backgroundIcon = subject { - skippedCollectionIds.insert(groupId) - continue - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: icon, - tintMode: tintMode - ) - - let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium - if isPremiumLocked && isPremiumDisabled { - continue - } - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - var title = "" - var headerItem: EntityKeyboardAnimationData? - inner: for (id, info, _) in view.collectionInfos { - if id == entry.index.collectionId, let info = info as? StickerPackCollectionInfo { - title = info.title - - if let thumbnail = info.thumbnail { - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - headerItem = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: info.immediateThumbnailData, - isReaction: false, - isTemplate: isTemplate - ) - } - - break inner - } - } - itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: headerItem, items: [resultItem])) - } - } - - if !isStandalone { - for featuredEmojiPack in featuredEmojiPacks { - if installedCollectionIds.contains(featuredEmojiPack.info.id) { - continue - } - - let supergroupId = featuredEmojiPack.info.id - let groupId: AnyHashable = supergroupId - - if skippedCollectionIds.contains(groupId) { - continue - } - - if case .channelStatus = subject { - if !featuredEmojiPack.info.flags.contains(.isAvailableAsChannelStatus) { - continue - } - } - - for item in featuredEmojiPack.topItems { - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - if [.status, .channelStatus, .backgroundIcon].contains(subject) { - if let backgroundIconColor { - tintMode = .custom(backgroundIconColor) - } else { - tintMode = .accent - } - } else { - tintMode = .primary - } - } else if case .backgroundIcon = subject { - skippedCollectionIds.insert(groupId) - continue - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium - if isPremiumLocked && isPremiumDisabled { - continue - } - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - var headerItem: EntityKeyboardAnimationData? - if let thumbnailFileId = featuredEmojiPack.info.thumbnailFileId, let file = featuredEmojiPack.topItems.first(where: { $0.file.fileId.id == thumbnailFileId }) { - headerItem = EntityKeyboardAnimationData(file: file.file) - } else if let thumbnail = featuredEmojiPack.info.thumbnail { - let info = featuredEmojiPack.info - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - headerItem = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: info.immediateThumbnailData, - isReaction: false, - isTemplate: false - ) - } - - var isFeatured = true - if case .reactionList = subject { - isFeatured = false - } - - itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: isFeatured, collapsedLineCount: 3, isClearable: false, headerItem: headerItem, items: [resultItem])) - } - } - } - } - } - - if hasPremium { - maybeAppendUnicodeEmoji() - } - - var displaySearchWithPlaceholder: String? - let searchInitiallyHidden = true - if hasSearch { - if [.reaction(onlyTop: false), .quickReaction].contains(subject) { - displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder - } else if case .status = subject { - displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder - } else if case .emoji = subject { - displaySearchWithPlaceholder = strings.EmojiSearch_SearchEmojiPlaceholder - } else if [.profilePhoto, .groupPhoto].contains(subject) { - displaySearchWithPlaceholder = strings.Common_Search - } - } - - let allItemGroups = itemGroups.map { group -> EmojiPagerContentComponent.ItemGroup in - var hasClear = group.isClearable - var isEmbedded = false - if group.id == AnyHashable("featuredTop") { - hasClear = true - isEmbedded = true - } - - var headerItem = group.headerItem - - if let groupId = group.id.base as? ItemCollectionId { - outer: for (id, info, _) in view.collectionInfos { - if id == groupId, let info = info as? StickerPackCollectionInfo { - if let thumbnailFileId = info.thumbnailFileId { - for item in group.items { - if let itemFile = item.itemFile, itemFile.fileId.id == thumbnailFileId { - headerItem = EntityKeyboardAnimationData(file: itemFile) - break outer - } - } - } - } - } - } - - return EmojiPagerContentComponent.ItemGroup( - supergroupId: group.supergroupId, - groupId: group.id, - title: group.title, - subtitle: group.subtitle, - actionButtonTitle: nil, - isFeatured: group.isFeatured, - isPremiumLocked: group.isPremiumLocked, - isEmbedded: isEmbedded, - hasClear: hasClear, - collapsedLineCount: group.collapsedLineCount, - displayPremiumBadges: false, - headerItem: headerItem, - fillWithLoadingPlaceholders: false, - customTintColor: backgroundIconColor, - items: group.items - ) - } - - let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .channelStatus, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) - let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status, .channelStatus].contains(subject) - - return EmojiPagerContentComponent( - id: "emoji", - context: context, - avatarPeer: nil, - animationCache: animationCache, - animationRenderer: animationRenderer, - inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder(), - panelItemGroups: allItemGroups, - contentItemGroups: allItemGroups, - itemLayoutType: .compact, - itemContentUniqueId: nil, - searchState: .empty(hasResults: false), - warpContentsOnEdges: warpContentsOnEdges, - hideBackground: hideBackground, - displaySearchWithPlaceholder: displaySearchWithPlaceholder, - searchCategories: searchCategories, - searchInitiallyHidden: searchInitiallyHidden, - searchAlwaysActive: false, - searchIsPlaceholderOnly: false, - emptySearchResults: nil, - enableLongPress: enableLongPress, - selectedItems: selectedItems, - customTintColor: backgroundIconColor - ) - } - return emojiItems - } - - public static func stickerInputData( - context: AccountContext, - animationCache: AnimationCache, - animationRenderer: MultiAnimationRenderer, - stickerNamespaces: [ItemCollectionId.Namespace], - stickerOrderedItemListCollectionIds: [Int32], - chatPeerId: EnginePeer.Id?, - hasSearch: Bool, - hasTrending: Bool, - forceHasPremium: Bool, - searchIsPlaceholderOnly: Bool = true, - isProfilePhotoEmojiSelection: Bool = false, - isGroupPhotoEmojiSelection: Bool = false, - hideBackground: Bool = false - ) -> Signal { - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - let isPremiumDisabled = premiumConfiguration.isPremiumDisabled - - struct PeerSpecificPackData: Equatable { - var info: StickerPackCollectionInfo - var items: [StickerPackItem] - var peer: EnginePeer - - static func ==(lhs: PeerSpecificPackData, rhs: PeerSpecificPackData) -> Bool { - if lhs.info.id != rhs.info.id { - return false - } - if lhs.items != rhs.items { - return false - } - if lhs.peer != rhs.peer { - return false - } - - return true - } - } - - let peerSpecificPack: Signal - if let chatPeerId = chatPeerId { - peerSpecificPack = combineLatest( - context.engine.peers.peerSpecificStickerPack(peerId: chatPeerId), - context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: chatPeerId)) - ) - |> map { packData, peer -> PeerSpecificPackData? in - guard let peer = peer else { - return nil - } - - guard let (info, items) = packData.packInfo else { - return nil - } - - return PeerSpecificPackData(info: info, items: items.compactMap { $0 as? StickerPackItem }, peer: peer) - } - |> distinctUntilChanged - } else { - peerSpecificPack = .single(nil) - } - - let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings - - let searchCategories: Signal - if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection { - searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) - } else { - searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) - } - return combineLatest( - context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000), - hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false), - hasTrending ? context.account.viewTracker.featuredStickerPacks() : .single([]), - context.engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.featuredStickersConfiguration, id: ValueBoxKey(length: 0))), - ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager), - peerSpecificPack, - searchCategories - ) - |> map { view, hasPremium, featuredStickerPacks, featuredStickersConfiguration, dismissedTrendingStickerPacks, peerSpecificPack, searchCategories -> EmojiPagerContentComponent in - let hasPremium = forceHasPremium || hasPremium - 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 headerItem: EntityKeyboardAnimationData? - var items: [EmojiPagerContentComponent.Item] - } - var itemGroups: [ItemGroup] = [] - var itemGroupIndexById: [AnyHashable: Int] = [:] - - var savedStickers: OrderedItemListView? - var recentStickers: OrderedItemListView? - for orderedView in view.orderedItemListsViews { - if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentStickers { - recentStickers = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudSavedStickers { - savedStickers = orderedView - } - } - - var installedCollectionIds = Set() - for (id, _, _) in view.collectionInfos { - installedCollectionIds.insert(id) - } - - let dismissedTrendingStickerPacksSet = Set(dismissedTrendingStickerPacks ?? []) - let featuredStickerPacksSet = Set(featuredStickerPacks.map(\.info.id.id)) - - if dismissedTrendingStickerPacksSet != featuredStickerPacksSet { - let featuredStickersConfiguration = featuredStickersConfiguration?.get(FeaturedStickersConfiguration.self) - for featuredStickerPack in featuredStickerPacks { - if installedCollectionIds.contains(featuredStickerPack.info.id) { - continue - } - - guard let item = featuredStickerPack.topItems.first else { - continue - } - - let animationData: EntityKeyboardAnimationData - - if let thumbnail = featuredStickerPack.info.thumbnail { - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - animationData = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(featuredStickerPack.info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: featuredStickerPack.info.immediateThumbnailData, - isReaction: false, - isTemplate: false - ) - } else { - animationData = EntityKeyboardAnimationData(file: item.file) - } - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let supergroupId = "featuredTop" - let groupId: AnyHashable = supergroupId - let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium - if isPremiumLocked && isPremiumDisabled { - continue - } - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - let trendingIsPremium = featuredStickersConfiguration?.isPremium ?? false - let title = trendingIsPremium ? strings.Stickers_TrendingPremiumStickers : strings.StickerPacksSettings_FeaturedPacks - - itemGroups.append( - ItemGroup( - supergroupId: groupId, - id: groupId, - title: title, - subtitle: nil, - actionButtonTitle: nil, - isPremiumLocked: false, - isFeatured: false, - displayPremiumBadges: false, - headerItem: nil, - items: [resultItem] - ) - ) - } - } - } - - if let savedStickers = savedStickers { - for item in savedStickers.items { - guard let item = item.contents.get(SavedStickerItem.self) else { - continue - } - if isPremiumDisabled && item.file.isPremiumSticker { - continue - } - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let groupId = "saved" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleFavoriteStickers, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) - } - } - } - - if let recentStickers = recentStickers { - for item in recentStickers.items { - guard let item = item.contents.get(RecentMediaItem.self) else { - continue - } - if isPremiumDisabled && item.media.isPremiumSticker { - continue - } - - var tintMode: Item.TintMode = .none - if item.media.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.media) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.media, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let groupId = "recent" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.Stickers_FrequentlyUsed, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) - } - } - } - - var avatarPeer: EnginePeer? - if let peerSpecificPack = peerSpecificPack { - avatarPeer = peerSpecificPack.peer - - var processedIds = Set() - for item in peerSpecificPack.items { - if isPremiumDisabled && item.file.isPremiumSticker { - continue - } - if processedIds.contains(item.file.fileId) { - continue - } - processedIds.insert(item.file.fileId) - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let groupId = "peerSpecific" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: peerSpecificPack.peer.compactDisplayTitle, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) - } - } - } - - for entry in view.entries { - guard let item = entry.item as? StickerPackItem else { - continue - } - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - let groupId = entry.index.collectionId - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - var title = "" - var headerItem: EntityKeyboardAnimationData? - inner: for (id, info, _) in view.collectionInfos { - if id == groupId, let info = info as? StickerPackCollectionInfo { - title = info.title - - if let thumbnail = info.thumbnail { - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - headerItem = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: info.immediateThumbnailData, - isReaction: false, - isTemplate: false - ) - } - - break inner - } - } - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: true, headerItem: headerItem, items: [resultItem])) - } - } - - for featuredStickerPack in featuredStickerPacks { - if installedCollectionIds.contains(featuredStickerPack.info.id) { - continue - } - - for item in featuredStickerPack.topItems { - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let supergroupId = featuredStickerPack.info.id - let groupId: AnyHashable = supergroupId - let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium - if isPremiumLocked && isPremiumDisabled { - continue - } - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - - let subtitle: String = strings.StickerPack_StickerCount(Int32(featuredStickerPack.info.count)) - var headerItem: EntityKeyboardAnimationData? - - if let thumbnailFileId = featuredStickerPack.info.thumbnailFileId, let file = featuredStickerPack.topItems.first(where: { $0.file.fileId.id == thumbnailFileId }) { - headerItem = EntityKeyboardAnimationData(file: file.file) - } else if let thumbnail = featuredStickerPack.info.thumbnail { - let info = featuredStickerPack.info - let type: EntityKeyboardAnimationData.ItemType - if item.file.isAnimatedSticker { - type = .lottie - } else if item.file.isVideoEmoji || item.file.isVideoSticker { - type = .video - } else { - type = .still - } - - headerItem = EntityKeyboardAnimationData( - id: .stickerPackThumbnail(info.id), - type: type, - resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), - dimensions: thumbnail.dimensions.cgSize, - immediateThumbnailData: info.immediateThumbnailData, - isReaction: false, - isTemplate: false - ) - } - - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: featuredStickerPack.info.title, subtitle: subtitle, actionButtonTitle: strings.Stickers_Install, isPremiumLocked: isPremiumLocked, isFeatured: true, displayPremiumBadges: false, headerItem: headerItem, items: [resultItem])) - } - } - } - - let isMasks = stickerNamespaces.contains(Namespaces.ItemCollection.CloudMaskPacks) - - let allItemGroups = itemGroups.map { group -> EmojiPagerContentComponent.ItemGroup in - var hasClear = false - var isEmbedded = false - if group.id == AnyHashable("recent") { - hasClear = true - } else if group.id == AnyHashable("featuredTop") { - hasClear = true - isEmbedded = true - } - - return EmojiPagerContentComponent.ItemGroup( - supergroupId: group.supergroupId, - groupId: group.id, - title: group.title, - subtitle: group.subtitle, - actionButtonTitle: group.actionButtonTitle, - isFeatured: group.isFeatured, - isPremiumLocked: group.isPremiumLocked, - isEmbedded: isEmbedded, - hasClear: hasClear, - collapsedLineCount: nil, - displayPremiumBadges: group.displayPremiumBadges, - headerItem: group.headerItem, - fillWithLoadingPlaceholders: false, - items: group.items - ) - } - - return EmojiPagerContentComponent( - id: isMasks ? "masks" : "stickers", - context: context, - avatarPeer: avatarPeer, - animationCache: animationCache, - animationRenderer: animationRenderer, - inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder(), - panelItemGroups: allItemGroups, - contentItemGroups: allItemGroups, - itemLayoutType: .detailed, - itemContentUniqueId: nil, - searchState: .empty(hasResults: false), - warpContentsOnEdges: isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection, - hideBackground: hideBackground, - displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, - searchCategories: searchCategories, - searchInitiallyHidden: true, - searchAlwaysActive: false, - searchIsPlaceholderOnly: searchIsPlaceholderOnly, - emptySearchResults: nil, - enableLongPress: false, - selectedItems: Set(), - customTintColor: nil - ) - } - } } func generateTopicIcon(backgroundColors: [UIColor], strokeColors: [UIColor], title: String) -> UIImage? { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift new file mode 100644 index 0000000000..98c3fb677c --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift @@ -0,0 +1,1950 @@ +import Foundation +import UIKit +import AccountContext +import TelegramCore +import Postbox +import SwiftSignalKit +import AnimationCache +import MultiAnimationRenderer +import TelegramNotices + +public extension EmojiPagerContentComponent { + private static func hasPremium(context: AccountContext, chatPeerId: EnginePeer.Id?, premiumIfSavedMessages: Bool) -> Signal { + let hasPremium: Signal + if premiumIfSavedMessages, let chatPeerId = chatPeerId, chatPeerId == context.account.peerId { + hasPremium = .single(true) + } else { + hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + guard case let .user(user) = peer else { + return false + } + return user.isPremium + } + |> distinctUntilChanged + } + return hasPremium + } + + enum Subject: Equatable { + case generic + case status + case channelStatus + case reaction(onlyTop: Bool) + case messageTag + case emoji + case topicIcon + case quickReaction + case profilePhoto + case groupPhoto + case backgroundIcon + case reactionList + } + + static func emojiInputData( + context: AccountContext, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + isStandalone: Bool, + subject: Subject, + hasTrending: Bool, + topReactionItems: [EmojiComponentReactionItem], + areUnicodeEmojiEnabled: Bool, + areCustomEmojiEnabled: Bool, + chatPeerId: EnginePeer.Id?, + selectedItems: Set = Set(), + topStatusTitle: String? = nil, + topicTitle: String? = nil, + topicColor: Int32? = nil, + backgroundIconColor: UIColor? = nil, + hasSearch: Bool = true, + forceHasPremium: Bool = false, + premiumIfSavedMessages: Bool = true, + 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 + + var orderedItemListCollectionIds: [Int32] = [] + + switch subject { + case .backgroundIcon, .reactionList: + break + default: + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.LocalRecentEmoji) + } + + var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([]) + + if case .status = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji) + + iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) + |> map { result -> [TelegramMediaFile] in + switch result { + case let .result(_, items, _): + return items.map(\.file) + default: + return [] + } + } + |> take(1) + } else if case .channelStatus = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedChannelStatusEmoji) + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudDisabledChannelStatusEmoji) + + iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconChannelStatusEmoji, forceActualized: false) + |> map { result -> [TelegramMediaFile] in + switch result { + case let .result(_, items, _): + return items.map(\.file) + default: + return [] + } + } + |> take(1) + } else if [.reaction(onlyTop: false), .quickReaction].contains(subject) { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions) + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions) + } else if case .messageTag = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudDefaultTagReactions) + } else if case .topicIcon = subject { + iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false) + |> map { result -> [TelegramMediaFile] in + switch result { + case let .result(_, items, _): + return items.map(\.file) + default: + return [] + } + } + |> take(1) + } else if case .profilePhoto = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji) + } else if case .groupPhoto = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji) + } else if case .backgroundIcon = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji) + } + + let availableReactions: Signal + if [.reaction(onlyTop: false), .quickReaction, .reactionList].contains(subject) { + availableReactions = context.engine.stickers.availableReactions() + } else { + availableReactions = .single(nil) + } + + let searchCategories: Signal + if [.emoji, .reaction(onlyTop: false), .reactionList, .messageTag].contains(subject) { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) + } else if case .status = subject { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) + } else if case .channelStatus = subject { + searchCategories = .single(nil) + } else if [.profilePhoto, .groupPhoto].contains(subject) { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) + } else { + searchCategories = .single(nil) + } + + let emojiItems: Signal = combineLatest( + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: orderedItemListCollectionIds, namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), + forceHasPremium ? .single(true) : hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: premiumIfSavedMessages), + context.account.viewTracker.featuredEmojiPacks(), + availableReactions, + searchCategories, + iconStatusEmoji, + ApplicationSpecificNotice.dismissedTrendingEmojiPacks(accountManager: context.sharedContext.accountManager) + ) + |> map { view, hasPremium, featuredEmojiPacks, availableReactions, searchCategories, iconStatusEmoji, dismissedTrendingEmojiPacks -> EmojiPagerContentComponent in + struct ItemGroup { + var supergroupId: AnyHashable + var id: AnyHashable + var title: String? + var subtitle: String? + var isPremiumLocked: Bool + var isFeatured: Bool + var collapsedLineCount: Int? + var isClearable: Bool + var headerItem: EntityKeyboardAnimationData? + var items: [EmojiPagerContentComponent.Item] + } + var itemGroups: [ItemGroup] = [] + var itemGroupIndexById: [AnyHashable: Int] = [:] + + let maybeAppendUnicodeEmoji = { + let groupId: AnyHashable = "static" + + if itemGroupIndexById[groupId] != nil { + return + } + + if areUnicodeEmojiEnabled { + for (subgroupId, list) in staticEmojiMapping { + for emojiString in list { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .staticEmoji(emojiString), + itemFile: nil, + subgroupId: subgroupId.rawValue, + icon: .none, + tintMode: .none + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } + } + + var installedCollectionIds = Set() + for (id, _, _) in view.collectionInfos { + installedCollectionIds.insert(id) + } + + let dismissedTrendingEmojiPacksSet = Set(dismissedTrendingEmojiPacks ?? []) + let featuredEmojiPacksSet = Set(featuredEmojiPacks.map(\.info.id.id)) + + if dismissedTrendingEmojiPacksSet != featuredEmojiPacksSet && hasTrending { + for featuredEmojiPack in featuredEmojiPacks { + if installedCollectionIds.contains(featuredEmojiPack.info.id) { + continue + } + + guard let item = featuredEmojiPack.topItems.first else { + continue + } + + let animationData: EntityKeyboardAnimationData + + if let thumbnail = featuredEmojiPack.info.thumbnail { + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + animationData = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(featuredEmojiPack.info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: featuredEmojiPack.info.id.id, accessHash: featuredEmojiPack.info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: featuredEmojiPack.info.immediateThumbnailData, + isReaction: false, + isTemplate: false + ) + } else { + animationData = EntityKeyboardAnimationData(file: item.file) + } + + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let supergroupId = "featuredTop" + let groupId: AnyHashable = supergroupId + let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium + if isPremiumLocked && isPremiumDisabled { + continue + } + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + let title = context.sharedContext.currentPresentationData.with({ $0 }).strings.EmojiInput_TrendingEmoji + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 0, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + + var recentEmoji: OrderedItemListView? + var featuredStatusEmoji: OrderedItemListView? + var featuredChannelStatusEmoji: OrderedItemListView? + var disabledChannelStatusEmoji: OrderedItemListView? + var recentStatusEmoji: OrderedItemListView? + var topReactions: OrderedItemListView? + var recentReactions: OrderedItemListView? + var featuredAvatarEmoji: OrderedItemListView? + var featuredBackgroundIconEmoji: OrderedItemListView? + var defaultTagReactions: OrderedItemListView? + for orderedView in view.orderedItemListsViews { + if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji { + recentEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedStatusEmoji { + featuredStatusEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedChannelStatusEmoji { + featuredChannelStatusEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudDisabledChannelStatusEmoji { + disabledChannelStatusEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentStatusEmoji { + recentStatusEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentReactions { + recentReactions = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudTopReactions { + topReactions = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji { + featuredAvatarEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji { + featuredAvatarEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji { + featuredBackgroundIconEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudDefaultTagReactions { + defaultTagReactions = orderedView + } + } + + if case .topicIcon = subject { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.topic(String((topicTitle ?? "").prefix(1)), topicColor ?? 0)), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .none + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + var existingIds = Set() + + for file in iconStatusEmoji { + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } + } + } else if case .status = subject { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.premiumStar), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .none + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + var existingIds = Set() + + for file in iconStatusEmoji.prefix(7) { + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } + } + + if let recentStatusEmoji = recentStatusEmoji { + for item in recentStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + if let featuredStatusEmoji = featuredStatusEmoji { + for item in featuredStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } else if case .channelStatus = subject { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.stop), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .accent + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + var existingIds = Set() + + if let disabledChannelStatusEmoji { + for item in disabledChannelStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + let file = item.media + existingIds.insert(file.fileId) + } + } + + for file in iconStatusEmoji { + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } + } + + if let featuredChannelStatusEmoji { + for item in featuredChannelStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + /*if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + }*/ + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } else if subject == .reactionList { + var existingIds = Set() + + if let availableReactions = availableReactions { + for reactionItem in availableReactions.reactions { + if !reactionItem.isEnabled { + continue + } + if existingIds.contains(reactionItem.value) { + continue + } + existingIds.insert(reactionItem.value) + + let icon: EmojiPagerContentComponent.Item.Icon + if !hasPremium, case .custom = reactionItem.value { + icon = .locked + } else { + icon = .none + } + + var tintMode: Item.TintMode = .none + if reactionItem.selectAnimation.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.selectAnimation + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "liked" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } else if [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction].contains(subject) { + var existingIds = Set() + + var topReactionItems = topReactionItems + if topReactionItems.isEmpty { + if let topReactions = topReactions { + for item in topReactions.items { + guard let topReaction = item.contents.get(RecentReactionItem.self) else { + continue + } + + switch topReaction.content { + case let .builtin(value): + if let reaction = availableReactions?.reactions.first(where: { $0.value == .builtin(value) }) { + topReactionItems.append(EmojiComponentReactionItem(reaction: .builtin(value), file: reaction.selectAnimation)) + } else { + continue + } + case let .custom(file): + topReactionItems.append(EmojiComponentReactionItem(reaction: .custom(file.fileId.id), file: file)) + } + } + } + } + + let maxTopLineCount: Int + if case .reaction(onlyTop: true) = subject { + maxTopLineCount = 1000 + } else if hasPremium { + maxTopLineCount = 2 + } else { + maxTopLineCount = 6 + } + + for reactionItem in topReactionItems { + if existingIds.contains(reactionItem.reaction) { + continue + } + existingIds.insert(reactionItem.reaction) + + let icon: EmojiPagerContentComponent.Item.Icon + if case .reaction(onlyTop: true) = subject { + icon = .none + } else if !hasPremium, case .custom = reactionItem.reaction { + icon = .locked + } else { + icon = .none + } + + var tintMode: Item.TintMode = .none + if reactionItem.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.file + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + + if itemGroups[groupIndex].items.count >= 8 * maxTopLineCount { + break + } + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + + if case .reaction(onlyTop: false) = subject { + var hasRecent = false + if let recentReactions = recentReactions, !recentReactions.items.isEmpty { + hasRecent = true + } + + let maxRecentLineCount: Int + if hasPremium { + maxRecentLineCount = 10 + } else { + maxRecentLineCount = 10 + } + + let popularTitle = hasRecent ? strings.Chat_ReactionSection_Recent : strings.Chat_ReactionSection_Popular + + if let availableReactions = availableReactions { + for reactionItem in availableReactions.reactions { + if !reactionItem.isEnabled { + continue + } + if existingIds.contains(reactionItem.value) { + continue + } + existingIds.insert(reactionItem.value) + + let icon: EmojiPagerContentComponent.Item.Icon + if !hasPremium, case .custom = reactionItem.value { + icon = .locked + } else { + icon = .none + } + + var tintMode: Item.TintMode = .none + if reactionItem.selectAnimation.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.selectAnimation + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + if hasPremium { + let groupId = "popular" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) + } + } else { + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + + if itemGroups[groupIndex].items.count >= maxRecentLineCount * 8 { + break + } + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } + + if let recentReactions = recentReactions { + var popularInsertIndex = 0 + for item in recentReactions.items { + guard let item = item.contents.get(RecentReactionItem.self) else { + continue + } + + let animationFile: TelegramMediaFile + let icon: EmojiPagerContentComponent.Item.Icon + + switch item.content { + case let .builtin(value): + if existingIds.contains(.builtin(value)) { + continue + } + existingIds.insert(.builtin(value)) + if let availableReactions = availableReactions, let availableReaction = availableReactions.reactions.first(where: { $0.value == .builtin(value) }) { + if let centerAnimation = availableReaction.centerAnimation { + animationFile = centerAnimation + } else { + continue + } + } else { + continue + } + + icon = .none + case let .custom(file): + if existingIds.contains(.custom(file.fileId.id)) { + continue + } + existingIds.insert(.custom(file.fileId.id)) + animationFile = file + + if !hasPremium { + icon = .locked + } else { + icon = .none + } + } + + var tintMode: Item.TintMode = .none + if animationFile.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "popular" + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count + 1 >= maxRecentLineCount * 8 { + break + } + + itemGroups[groupIndex].items.insert(resultItem, at: popularInsertIndex) + popularInsertIndex += 1 + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) + } + } + } + } + } else if case .messageTag = subject { + var existingIds = Set() + + var topReactionItems = topReactionItems + if topReactionItems.isEmpty { + if let defaultTagReactions { + for item in defaultTagReactions.items { + guard let topReaction = item.contents.get(RecentReactionItem.self) else { + continue + } + + switch topReaction.content { + case let .builtin(value): + if let reaction = availableReactions?.reactions.first(where: { $0.value == .builtin(value) }) { + topReactionItems.append(EmojiComponentReactionItem(reaction: .builtin(value), file: reaction.selectAnimation)) + } else { + continue + } + case let .custom(file): + topReactionItems.append(EmojiComponentReactionItem(reaction: .custom(file.fileId.id), file: file)) + } + } + } + } + + let maxTopLineCount: Int = 1000 + + for reactionItem in topReactionItems { + if existingIds.contains(reactionItem.reaction) { + continue + } + existingIds.insert(reactionItem.reaction) + + let icon: EmojiPagerContentComponent.Item.Icon = .none + + var tintMode: Item.TintMode = .none + if reactionItem.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationFile = reactionItem.file + let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: animationFile, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + + if itemGroups[groupIndex].items.count >= 8 * maxTopLineCount { + break + } + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } else if [.profilePhoto, .groupPhoto].contains(subject) { + var existingIds = Set() + + let groupId = "recent" + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [])) + + if let featuredAvatarEmoji = featuredAvatarEmoji { + for item in featuredAvatarEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } else if case .backgroundIcon = subject { + var existingIds = Set() + + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.stop), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .accent + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + if let featuredBackgroundIconEmoji { + for item in featuredBackgroundIconEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } + + let hasRecentEmoji = ![.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon, .reactionList].contains(subject) + + if let recentEmoji = recentEmoji, hasRecentEmoji { + for item in recentEmoji.items { + guard let item = item.contents.get(RecentEmojiItem.self) else { + continue + } + + if case let .file(file) = item.content, isPremiumDisabled, file.isPremiumEmoji { + continue + } + + if !areCustomEmojiEnabled, case .file = item.content { + continue + } + + let resultItem: EmojiPagerContentComponent.Item + switch item.content { + case let .file(file): + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + case let .text(text): + resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .staticEmoji(text), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .none + ) + } + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.Emoji_FrequentlyUsed, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: true, headerItem: nil, items: [resultItem])) + } + } + } + + if !hasPremium { + maybeAppendUnicodeEmoji() + } + + var itemCollectionMapping: [ItemCollectionId: StickerPackCollectionInfo] = [:] + for (id, info, _) in view.collectionInfos { + if let info = info as? StickerPackCollectionInfo { + itemCollectionMapping[id] = info + } + } + + var skippedCollectionIds = Set() + if areCustomEmojiEnabled { + for entry in view.entries { + guard let item = entry.item as? StickerPackItem else { + continue + } + + var icon: EmojiPagerContentComponent.Item.Icon = .none + if [.reaction(onlyTop: false), .quickReaction].contains(subject), !hasPremium { + icon = .locked + } + + let supergroupId = entry.index.collectionId + let groupId: AnyHashable = supergroupId + + if skippedCollectionIds.contains(groupId) { + continue + } + + if case .channelStatus = subject { + guard let collection = itemCollectionMapping[entry.index.collectionId] else { + continue + } + if !collection.flags.contains(.isAvailableAsChannelStatus) { + continue + } + } + + var isTemplate = false + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + if [.status, .channelStatus, .backgroundIcon].contains(subject) { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } + } else { + tintMode = .primary + } + if case .backgroundIcon = subject { + isTemplate = true + } + } else if case .backgroundIcon = subject { + skippedCollectionIds.insert(groupId) + continue + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: icon, + tintMode: tintMode + ) + + let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium + if isPremiumLocked && isPremiumDisabled { + continue + } + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + var title = "" + var headerItem: EntityKeyboardAnimationData? + inner: for (id, info, _) in view.collectionInfos { + if id == entry.index.collectionId, let info = info as? StickerPackCollectionInfo { + title = info.title + + if let thumbnail = info.thumbnail { + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + headerItem = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: info.immediateThumbnailData, + isReaction: false, + isTemplate: isTemplate + ) + } + + break inner + } + } + itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: headerItem, items: [resultItem])) + } + } + + if !isStandalone { + for featuredEmojiPack in featuredEmojiPacks { + if installedCollectionIds.contains(featuredEmojiPack.info.id) { + continue + } + + let supergroupId = featuredEmojiPack.info.id + let groupId: AnyHashable = supergroupId + + if skippedCollectionIds.contains(groupId) { + continue + } + + if case .channelStatus = subject { + if !featuredEmojiPack.info.flags.contains(.isAvailableAsChannelStatus) { + continue + } + } + + for item in featuredEmojiPack.topItems { + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + if [.status, .channelStatus, .backgroundIcon].contains(subject) { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } + } else { + tintMode = .primary + } + } else if case .backgroundIcon = subject { + skippedCollectionIds.insert(groupId) + continue + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium + if isPremiumLocked && isPremiumDisabled { + continue + } + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + var headerItem: EntityKeyboardAnimationData? + if let thumbnailFileId = featuredEmojiPack.info.thumbnailFileId, let file = featuredEmojiPack.topItems.first(where: { $0.file.fileId.id == thumbnailFileId }) { + headerItem = EntityKeyboardAnimationData(file: file.file) + } else if let thumbnail = featuredEmojiPack.info.thumbnail { + let info = featuredEmojiPack.info + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + headerItem = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: info.immediateThumbnailData, + isReaction: false, + isTemplate: false + ) + } + + var isFeatured = true + if case .reactionList = subject { + isFeatured = false + } + + itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: isFeatured, collapsedLineCount: 3, isClearable: false, headerItem: headerItem, items: [resultItem])) + } + } + } + } + } + + if hasPremium { + maybeAppendUnicodeEmoji() + } + + var displaySearchWithPlaceholder: String? + let searchInitiallyHidden = true + if hasSearch { + if [.reaction(onlyTop: false), .quickReaction].contains(subject) { + displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder + } else if case .status = subject { + displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder + } else if case .emoji = subject { + displaySearchWithPlaceholder = strings.EmojiSearch_SearchEmojiPlaceholder + } else if [.profilePhoto, .groupPhoto].contains(subject) { + displaySearchWithPlaceholder = strings.Common_Search + } + } + + let allItemGroups = itemGroups.map { group -> EmojiPagerContentComponent.ItemGroup in + var hasClear = group.isClearable + var isEmbedded = false + if group.id == AnyHashable("featuredTop") { + hasClear = true + isEmbedded = true + } + + var headerItem = group.headerItem + + if let groupId = group.id.base as? ItemCollectionId { + outer: for (id, info, _) in view.collectionInfos { + if id == groupId, let info = info as? StickerPackCollectionInfo { + if let thumbnailFileId = info.thumbnailFileId { + for item in group.items { + if let itemFile = item.itemFile, itemFile.fileId.id == thumbnailFileId { + headerItem = EntityKeyboardAnimationData(file: itemFile) + break outer + } + } + } + } + } + } + + return EmojiPagerContentComponent.ItemGroup( + supergroupId: group.supergroupId, + groupId: group.id, + title: group.title, + subtitle: group.subtitle, + actionButtonTitle: nil, + isFeatured: group.isFeatured, + isPremiumLocked: group.isPremiumLocked, + isEmbedded: isEmbedded, + hasClear: hasClear, + collapsedLineCount: group.collapsedLineCount, + displayPremiumBadges: false, + headerItem: headerItem, + fillWithLoadingPlaceholders: false, + customTintColor: backgroundIconColor, + items: group.items + ) + } + + let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .channelStatus, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) + let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status, .channelStatus].contains(subject) + + return EmojiPagerContentComponent( + id: "emoji", + context: context, + avatarPeer: nil, + animationCache: animationCache, + animationRenderer: animationRenderer, + inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder(), + panelItemGroups: allItemGroups, + contentItemGroups: allItemGroups, + itemLayoutType: .compact, + itemContentUniqueId: nil, + searchState: .empty(hasResults: false), + warpContentsOnEdges: warpContentsOnEdges, + hideBackground: hideBackground, + displaySearchWithPlaceholder: displaySearchWithPlaceholder, + searchCategories: searchCategories, + searchInitiallyHidden: searchInitiallyHidden, + searchAlwaysActive: false, + searchIsPlaceholderOnly: false, + emptySearchResults: nil, + enableLongPress: enableLongPress, + selectedItems: selectedItems, + customTintColor: backgroundIconColor + ) + } + return emojiItems + } + + static func stickerInputData( + context: AccountContext, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + stickerNamespaces: [ItemCollectionId.Namespace], + stickerOrderedItemListCollectionIds: [Int32], + chatPeerId: EnginePeer.Id?, + hasSearch: Bool, + hasTrending: Bool, + forceHasPremium: Bool, + searchIsPlaceholderOnly: Bool = true, + isProfilePhotoEmojiSelection: Bool = false, + isGroupPhotoEmojiSelection: Bool = false, + hideBackground: Bool = false + ) -> Signal { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let isPremiumDisabled = premiumConfiguration.isPremiumDisabled + + struct PeerSpecificPackData: Equatable { + var info: StickerPackCollectionInfo + var items: [StickerPackItem] + var peer: EnginePeer + + static func ==(lhs: PeerSpecificPackData, rhs: PeerSpecificPackData) -> Bool { + if lhs.info.id != rhs.info.id { + return false + } + if lhs.items != rhs.items { + return false + } + if lhs.peer != rhs.peer { + return false + } + + return true + } + } + + let peerSpecificPack: Signal + if let chatPeerId = chatPeerId { + peerSpecificPack = combineLatest( + context.engine.peers.peerSpecificStickerPack(peerId: chatPeerId), + context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: chatPeerId)) + ) + |> map { packData, peer -> PeerSpecificPackData? in + guard let peer = peer else { + return nil + } + + guard let (info, items) = packData.packInfo else { + return nil + } + + return PeerSpecificPackData(info: info, items: items.compactMap { $0 as? StickerPackItem }, peer: peer) + } + |> distinctUntilChanged + } else { + peerSpecificPack = .single(nil) + } + + let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings + + let searchCategories: Signal + if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) + } else { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) + } + return combineLatest( + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000), + hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false), + hasTrending ? context.account.viewTracker.featuredStickerPacks() : .single([]), + context.engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.featuredStickersConfiguration, id: ValueBoxKey(length: 0))), + ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager), + peerSpecificPack, + searchCategories + ) + |> map { view, hasPremium, featuredStickerPacks, featuredStickersConfiguration, dismissedTrendingStickerPacks, peerSpecificPack, searchCategories -> EmojiPagerContentComponent in + let hasPremium = forceHasPremium || hasPremium + 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 headerItem: EntityKeyboardAnimationData? + var items: [EmojiPagerContentComponent.Item] + } + var itemGroups: [ItemGroup] = [] + var itemGroupIndexById: [AnyHashable: Int] = [:] + + var savedStickers: OrderedItemListView? + var recentStickers: OrderedItemListView? + for orderedView in view.orderedItemListsViews { + if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentStickers { + recentStickers = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudSavedStickers { + savedStickers = orderedView + } + } + + var installedCollectionIds = Set() + for (id, _, _) in view.collectionInfos { + installedCollectionIds.insert(id) + } + + let dismissedTrendingStickerPacksSet = Set(dismissedTrendingStickerPacks ?? []) + let featuredStickerPacksSet = Set(featuredStickerPacks.map(\.info.id.id)) + + if dismissedTrendingStickerPacksSet != featuredStickerPacksSet { + let featuredStickersConfiguration = featuredStickersConfiguration?.get(FeaturedStickersConfiguration.self) + for featuredStickerPack in featuredStickerPacks { + if installedCollectionIds.contains(featuredStickerPack.info.id) { + continue + } + + guard let item = featuredStickerPack.topItems.first else { + continue + } + + let animationData: EntityKeyboardAnimationData + + if let thumbnail = featuredStickerPack.info.thumbnail { + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + animationData = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(featuredStickerPack.info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: featuredStickerPack.info.immediateThumbnailData, + isReaction: false, + isTemplate: false + ) + } else { + animationData = EntityKeyboardAnimationData(file: item.file) + } + + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let supergroupId = "featuredTop" + let groupId: AnyHashable = supergroupId + let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium + if isPremiumLocked && isPremiumDisabled { + continue + } + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + let trendingIsPremium = featuredStickersConfiguration?.isPremium ?? false + let title = trendingIsPremium ? strings.Stickers_TrendingPremiumStickers : strings.StickerPacksSettings_FeaturedPacks + + itemGroups.append( + ItemGroup( + supergroupId: groupId, + id: groupId, + title: title, + subtitle: nil, + actionButtonTitle: nil, + isPremiumLocked: false, + isFeatured: false, + displayPremiumBadges: false, + headerItem: nil, + items: [resultItem] + ) + ) + } + } + } + + if let savedStickers = savedStickers { + for item in savedStickers.items { + guard let item = item.contents.get(SavedStickerItem.self) else { + continue + } + if isPremiumDisabled && item.file.isPremiumSticker { + continue + } + + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let groupId = "saved" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleFavoriteStickers, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) + } + } + } + + if let recentStickers = recentStickers { + for item in recentStickers.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + if isPremiumDisabled && item.media.isPremiumSticker { + continue + } + + var tintMode: Item.TintMode = .none + if item.media.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.media) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.media, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.Stickers_FrequentlyUsed, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) + } + } + } + + var avatarPeer: EnginePeer? + if let peerSpecificPack = peerSpecificPack { + avatarPeer = peerSpecificPack.peer + + var processedIds = Set() + for item in peerSpecificPack.items { + if isPremiumDisabled && item.file.isPremiumSticker { + continue + } + if processedIds.contains(item.file.fileId) { + continue + } + processedIds.insert(item.file.fileId) + + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let groupId = "peerSpecific" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: peerSpecificPack.peer.compactDisplayTitle, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) + } + } + } + + for entry in view.entries { + guard let item = entry.item as? StickerPackItem else { + continue + } + + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + let groupId = entry.index.collectionId + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + var title = "" + var headerItem: EntityKeyboardAnimationData? + inner: for (id, info, _) in view.collectionInfos { + if id == groupId, let info = info as? StickerPackCollectionInfo { + title = info.title + + if let thumbnail = info.thumbnail { + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + headerItem = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: info.immediateThumbnailData, + isReaction: false, + isTemplate: false + ) + } + + break inner + } + } + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: true, headerItem: headerItem, items: [resultItem])) + } + } + + for featuredStickerPack in featuredStickerPacks { + if installedCollectionIds.contains(featuredStickerPack.info.id) { + continue + } + + for item in featuredStickerPack.topItems { + var tintMode: Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + let supergroupId = featuredStickerPack.info.id + let groupId: AnyHashable = supergroupId + let isPremiumLocked: Bool = item.file.isPremiumSticker && !hasPremium + if isPremiumLocked && isPremiumDisabled { + continue + } + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + + let subtitle: String = strings.StickerPack_StickerCount(Int32(featuredStickerPack.info.count)) + var headerItem: EntityKeyboardAnimationData? + + if let thumbnailFileId = featuredStickerPack.info.thumbnailFileId, let file = featuredStickerPack.topItems.first(where: { $0.file.fileId.id == thumbnailFileId }) { + headerItem = EntityKeyboardAnimationData(file: file.file) + } else if let thumbnail = featuredStickerPack.info.thumbnail { + let info = featuredStickerPack.info + let type: EntityKeyboardAnimationData.ItemType + if item.file.isAnimatedSticker { + type = .lottie + } else if item.file.isVideoEmoji || item.file.isVideoSticker { + type = .video + } else { + type = .still + } + + headerItem = EntityKeyboardAnimationData( + id: .stickerPackThumbnail(info.id), + type: type, + resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), + dimensions: thumbnail.dimensions.cgSize, + immediateThumbnailData: info.immediateThumbnailData, + isReaction: false, + isTemplate: false + ) + } + + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: featuredStickerPack.info.title, subtitle: subtitle, actionButtonTitle: strings.Stickers_Install, isPremiumLocked: isPremiumLocked, isFeatured: true, displayPremiumBadges: false, headerItem: headerItem, items: [resultItem])) + } + } + } + + let isMasks = stickerNamespaces.contains(Namespaces.ItemCollection.CloudMaskPacks) + + let allItemGroups = itemGroups.map { group -> EmojiPagerContentComponent.ItemGroup in + var hasClear = false + var isEmbedded = false + if group.id == AnyHashable("recent") { + hasClear = true + } else if group.id == AnyHashable("featuredTop") { + hasClear = true + isEmbedded = true + } + + return EmojiPagerContentComponent.ItemGroup( + supergroupId: group.supergroupId, + groupId: group.id, + title: group.title, + subtitle: group.subtitle, + actionButtonTitle: group.actionButtonTitle, + isFeatured: group.isFeatured, + isPremiumLocked: group.isPremiumLocked, + isEmbedded: isEmbedded, + hasClear: hasClear, + collapsedLineCount: nil, + displayPremiumBadges: group.displayPremiumBadges, + headerItem: group.headerItem, + fillWithLoadingPlaceholders: false, + items: group.items + ) + } + + return EmojiPagerContentComponent( + id: isMasks ? "masks" : "stickers", + context: context, + avatarPeer: avatarPeer, + animationCache: animationCache, + animationRenderer: animationRenderer, + inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder(), + panelItemGroups: allItemGroups, + contentItemGroups: allItemGroups, + itemLayoutType: .detailed, + itemContentUniqueId: nil, + searchState: .empty(hasResults: false), + warpContentsOnEdges: isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection, + hideBackground: hideBackground, + displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil, + searchCategories: searchCategories, + searchInitiallyHidden: true, + searchAlwaysActive: false, + searchIsPlaceholderOnly: searchIsPlaceholderOnly, + emptySearchResults: nil, + enableLongPress: false, + selectedItems: Set(), + customTintColor: nil + ) + } + } +} diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift index cac1f69208..6e79e1af54 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift @@ -289,7 +289,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: false, isUnseen: false, isMy: true, peerId: accountPeer.id, timestamp: nil)) peers[accountPeer.id] = accountPeer } - attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: recentPeers)) + attributes.append(ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: recentPeers)) } let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true, isPreview: true) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index 02f609d4db..93cd9f7688 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -110,7 +110,7 @@ extension ChatControllerImpl { if canAddMessageReactions(message: topMessage), let allowedReactions = allowedReactions, !topReactions.isEmpty { actions.reactionItems = topReactions.map(ReactionContextItem.reaction) - if case .peer(self.context.account.peerId) = self.presentationInterfaceState.chatLocation { + if message.areReactionsTags(accountPeerId: self.context.account.peerId) { //TODO:localize actions.reactionsTitle = "Tag the message with an emoji for quick access later" } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index ce0c0ba193..f9341165e2 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -15796,13 +15796,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } switch search.domain { case .everything: - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, reactions: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case .members: derivedSearchState = nil case let .member(peer): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) - case let .tag(tag): - derivedSearchState = ChatSearchState(query: "@#\(tag) " + search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil, reactions: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + case let .tag(tag, _): + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, reactions: [tag], threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) } } @@ -15813,10 +15813,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if previousSearchState?.query != searchState.query || previousSearchState?.location != searchState.location { var queryIsEmpty = false if searchState.query.isEmpty { - if case let .peer(_, fromId, _, _, _, _) = searchState.location { + if case let .peer(_, fromId, _, reactions, _, _, _) = searchState.location { if fromId == nil { queryIsEmpty = true } + if let reactions, !reactions.isEmpty { + queryIsEmpty = false + } } else { queryIsEmpty = true } @@ -19179,6 +19182,10 @@ enum AllowedReactions { } func peerMessageAllowedReactions(context: AccountContext, message: Message) -> Signal { + if message.id.peerId == context.account.peerId { + return .single(.all) + } + if message.containsSecretMedia { return .single(AllowedReactions.set(Set())) } diff --git a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift index 38ffcd0589..d7988cf0a0 100644 --- a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift @@ -120,12 +120,12 @@ final class ChatSearchNavigationContentNode: NavigationBarContentNode { } } self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) - case let .tag(tag): + case let .tag(tag, file): //TODO:localize let placeholderText = "Search" self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) //TODO:localize - self.searchBar.tokens = [SearchBarToken(id: AnyHashable(tag), icon: nil, isTag: true, title: "\(tag) Tag", permanent: false)] + self.searchBar.tokens = [SearchBarToken(id: AnyHashable(tag), context: self.context, icon: nil, isTag: true, emojiFile: file, title: "Tag", permanent: false)] case .members: self.searchBar.tokens = [] self.searchBar.prefixString = NSAttributedString(string: strings.Conversation_SearchByName_Prefix, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputTextColor) diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 371f10956c..74117ec7f3 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -175,7 +175,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe self.searchResult = searchResult self.searchState = searchState - if case let .peer(peerId, _, _, _, _, _) = location, peerId == context.account.peerId { + if case let .peer(peerId, _, _, _, _, _, _) = location, peerId == context.account.peerId { self.mappedLocation = .savedMessagesChats } else { self.mappedLocation = .chatList(groupId: .root) diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index 84e24f7e6f..8f68249a8e 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -9,24 +9,61 @@ import ComponentFlow import MultilineTextComponent import PlainButtonComponent import UIKitRuntimeUtils +import TelegramCore +import EmojiStatusComponent +import SwiftSignalKit final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UIScrollViewDelegate { - private final class Item { - let tag: String + private struct Params: Equatable { + var width: CGFloat + var leftInset: CGFloat + var rightInset: CGFloat + var interfaceState: ChatPresentationInterfaceState - init(tag: String) { - self.tag = tag + init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, interfaceState: ChatPresentationInterfaceState) { + self.width = width + self.leftInset = leftInset + self.rightInset = rightInset + self.interfaceState = interfaceState + } + + static func ==(lhs: Params, rhs: Params) -> Bool { + if lhs.width != rhs.width { + return false + } + if lhs.leftInset != rhs.leftInset { + return false + } + if lhs.rightInset != rhs.rightInset { + return false + } + if lhs.interfaceState != rhs.interfaceState { + return false + } + return true + } + } + + private final class Item { + let reaction: MessageReaction.Reaction + let count: Int + let file: TelegramMediaFile + + init(reaction: MessageReaction.Reaction, count: Int, file: TelegramMediaFile) { + self.reaction = reaction + self.count = count + self.file = file } } private final class ItemView: UIView { private let context: AccountContext private let item: Item - private let action: (String) -> Void + private let action: () -> Void private let view = ComponentView() - init(context: AccountContext, item: Item, action: @escaping ((String) -> Void)) { + init(context: AccountContext, item: Item, action: @escaping (() -> Void)) { self.context = context self.item = item self.action = action @@ -42,23 +79,41 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc let viewSize = self.view.update( transition: transition, component: AnyComponent(PlainButtonComponent( - content: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: self.item.tag, font: Font.regular(15.0), textColor: theme.rootController.navigationBar.primaryTextColor)), - insets: UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0) - )), + content: AnyComponent(HStack([ + AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiStatusComponent( + context: self.context, + animationCache: self.context.animationCache, + animationRenderer: self.context.animationRenderer, + content: .animation( + content: .file(file: self.item.file), + size: CGSize(width: 32.0, height: 32.0), + placeholderColor: theme.list.mediaPlaceholderColor, + themeColor: theme.list.itemPrimaryTextColor, + loopMode: .forever + ), + size: CGSize(width: 16.0, height: 16.0), + isVisibleForAnimations: false, + useSharedAnimation: true, + action: nil, + emojiFileUpdated: nil + ))), + AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: "\(self.item.count)", font: Font.regular(15.0), textColor: theme.rootController.navigationBar.secondaryTextColor)) + ))) + ], spacing: 4.0)), effectAlignment: .center, minSize: CGSize(width: 0.0, height: height), - contentInsets: UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0), + contentInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), action: { [weak self] in guard let self else { return } - self.action(self.item.tag) + self.action() }, isEnabled: true )), environment: {}, - containerSize: CGSize(width: 100.0, height: 100.0) + containerSize: CGSize(width: 100.0, height: 32.0) ) if let componentView = self.view.view { if componentView.superview == nil { @@ -81,30 +136,21 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc } private let context: AccountContext - private var theme: PresentationTheme? - private var strings: PresentationStrings? private let scrollView: ScrollView - private let itemViews: [ItemView] + + private var params: Params? + + private var items: [Item] = [] + private var itemViews: [MessageReaction.Reaction: ItemView] = [:] + + private var itemsDisposable: Disposable? init(context: AccountContext) { self.context = context self.scrollView = ScrollView(frame: CGRect()) - let tags: [String] = [ - "⭐️", "❤️", "✅", "⏰", "💭", "❗️", "👍", "👎", "🤩", "⚡️", "🤡", "👌", "👏" - ] - let items = tags.map { - Item(tag: $0) - } - var itemAction: ((String) -> Void)? - self.itemViews = items.map { item in - return ItemView(context: context, item: item, action: { tag in - itemAction?(tag) - }) - } - super.init() self.scrollView.delaysContentTouches = false @@ -125,58 +171,105 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc self.scrollView.disablesInteractiveTransitionGestureRecognizer = true - for itemView in self.itemViews { - self.scrollView.addSubview(itemView) - } - - itemAction = { [weak self] tag in - guard let self, let interfaceInteraction = self.interfaceInteraction else { + self.itemsDisposable = (context.engine.stickers.savedMessageTags() + |> deliverOnMainQueue).start(next: { [weak self] tags, files in + guard let self else { return } - interfaceInteraction.beginMessageSearch(.tag(tag), "") + self.items = tags.compactMap { tag -> Item? in + switch tag.reaction { + case .builtin: + return nil + case let .custom(fileId): + guard let file = files[fileId] else { + return nil + } + return Item(reaction: tag.reaction, count: tag.count, file: file) + } + } + self.update(transition: .immediate) + }) + } + + deinit { + self.itemsDisposable?.dispose() + } + + private func update(transition: ContainedViewLayoutTransition) { + if let params = self.params { + self.update(params: params, transition: transition) } } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { - if interfaceState.strings !== self.strings { - self.strings = interfaceState.strings - } - - if interfaceState.theme !== self.theme { - self.theme = interfaceState.theme + let params = Params(width: width, leftInset: leftInset, rightInset: rightInset, interfaceState: interfaceState) + if self.params != params { + self.params = params + self.update(params: params, transition: transition) } let panelHeight: CGFloat = 33.0 - let containerInsets = UIEdgeInsets(top: 0.0, left: leftInset + 2.0, bottom: 0.0, right: rightInset + 2.0) + return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) + } + + private func update(params: Params, transition: ContainedViewLayoutTransition) { + let panelHeight: CGFloat = 33.0 + + let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 2.0, bottom: 0.0, right: params.rightInset + 2.0) let itemSpacing: CGFloat = 2.0 var contentSize = CGSize(width: 0.0, height: panelHeight) contentSize.width += containerInsets.left + var validIds: [MessageReaction.Reaction] = [] var isFirst = true - for itemView in self.itemViews { + for item in self.items { if isFirst { isFirst = false } else { contentSize.width += itemSpacing } + let itemId = item.reaction + validIds.append(itemId) - let itemSize = itemView.update(theme: interfaceState.theme, height: panelHeight, transition: .immediate) + let itemView: ItemView + if let current = self.itemViews[itemId] { + itemView = current + } else { + itemView = ItemView(context: self.context, item: item, action: { [weak self] in + guard let self else { + return + } + self.interfaceInteraction?.beginMessageSearch(.tag(item.reaction, item.file), "") + }) + self.itemViews[itemId] = itemView + self.scrollView.addSubview(itemView) + } + + let itemSize = itemView.update(theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) itemView.frame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: itemSize) contentSize.width += itemSize.width } + var removedIds: [MessageReaction.Reaction] = [] + for (id, itemView) in self.itemViews { + if !validIds.contains(id) { + removedIds.append(id) + itemView.removeFromSuperview() + } + } + for id in removedIds { + self.itemViews.removeValue(forKey: id) + } contentSize.width += containerInsets.right - let scrollSize = CGSize(width: width, height: contentSize.height) + let scrollSize = CGSize(width: params.width, height: contentSize.height) if self.scrollView.bounds.size != scrollSize { self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: -5.0), size: scrollSize) } if self.scrollView.contentSize != contentSize { self.scrollView.contentSize = contentSize } - - return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) } } diff --git a/submodules/TelegramUI/Sources/TopMessageReactions.swift b/submodules/TelegramUI/Sources/TopMessageReactions.swift index d323ec5f6c..fb58a030b4 100644 --- a/submodules/TelegramUI/Sources/TopMessageReactions.swift +++ b/submodules/TelegramUI/Sources/TopMessageReactions.swift @@ -6,6 +6,91 @@ import AccountContext import ReactionSelectionNode func topMessageReactions(context: AccountContext, message: Message) -> Signal<[ReactionItem], NoError> { + if message.id.peerId == context.account.peerId { + var loadTags = true + if let effectiveReactionsAttribute = message.effectiveReactionsAttribute { + if !effectiveReactionsAttribute.reactions.isEmpty { + if !effectiveReactionsAttribute.isTags { + loadTags = false + } + } + } + + if loadTags { + return combineLatest( + context.engine.stickers.availableReactions(), + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudDefaultTagReactions], namespaces: [ItemCollectionId.Namespace.max - 1], aroundIndex: nil, count: 10000000) + ) + |> take(1) + |> map { availableReactions, view -> [ReactionItem] in + var defaultTagReactions: OrderedItemListView? + for orderedView in view.orderedItemListsViews { + if orderedView.collectionId == Namespaces.OrderedItemList.CloudDefaultTagReactions { + defaultTagReactions = orderedView + } + } + + var result: [ReactionItem] = [] + var existingIds = Set() + + if let defaultTagReactions { + for item in defaultTagReactions.items { + guard let topReaction = item.contents.get(RecentReactionItem.self) else { + continue + } + switch topReaction.content { + case let .builtin(value): + if let reaction = availableReactions?.reactions.first(where: { $0.value == .builtin(value) }) { + guard let centerAnimation = reaction.centerAnimation else { + continue + } + guard let aroundAnimation = reaction.aroundAnimation else { + continue + } + + if existingIds.contains(reaction.value) { + continue + } + existingIds.insert(reaction.value) + + 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 + )) + } else { + continue + } + case let .custom(file): + if existingIds.contains(.custom(file.fileId.id)) { + continue + } + existingIds.insert(.custom(file.fileId.id)) + + result.append(ReactionItem( + reaction: ReactionItem.Reaction(rawValue: .custom(file.fileId.id)), + appearAnimation: file, + stillAnimation: file, + listAnimation: file, + largeListAnimation: file, + applicationAnimation: nil, + largeApplicationAnimation: nil, + isCustom: true + )) + } + } + } + + return result + } + } + } + let viewKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudTopReactions) let topReactions = context.account.postbox.combinedView(keys: [viewKey]) |> map { views -> [RecentReactionItem] in @@ -51,7 +136,6 @@ func topMessageReactions(context: AccountContext, message: Message) -> Signal<[R } var result: [ReactionItem] = [] - var existingIds = Set() for topReaction in topReactions { diff --git a/submodules/WatchBridge/Sources/WatchBridge.swift b/submodules/WatchBridge/Sources/WatchBridge.swift index 4cceb2c348..b89e103597 100644 --- a/submodules/WatchBridge/Sources/WatchBridge.swift +++ b/submodules/WatchBridge/Sources/WatchBridge.swift @@ -437,7 +437,7 @@ func makeBridgeUser(_ peer: Peer?, presence: PeerPresence? = nil, cachedData: Ca bridgeUser.lastSeen = -3 case .lastMonth: bridgeUser.lastSeen = -4 - case .none: + case .none, .hidden: bridgeUser.lastSeen = -5 case let .present(statusTimestamp): if statusTimestamp > timestamp { From 6a89f938db4335c024092b827cd5c81d9b5e813e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 6 Jan 2024 00:43:58 +0400 Subject: [PATCH 2/6] [WIP] Saved messages --- .../Sources/ChatListSearchListPaneNode.swift | 5 + .../Sources/ChatListShimmerNode.swift | 1 + .../ContextUI/Sources/ContextController.swift | 4 + .../ContextControllerActionsStackNode.swift | 51 +++- ...tControllerExtractedPresentationNode.swift | 1 + .../Sources/DrawingReactionView.swift | 1 + .../Sources/StickerPickerScreen.swift | 2 +- .../Sources/InstantPageControllerNode.swift | 2 +- .../Sources/MediaPickerSelectedListNode.swift | 4 +- .../Postbox/Sources/ChatListTable.swift | 2 +- .../Postbox/Sources/ChatListViewState.swift | 8 +- submodules/Postbox/Sources/Coding.swift | 28 +- .../Sources/GlobalMessageTagsView.swift | 4 +- .../Postbox/Sources/IntermediateMessage.swift | 12 +- ...idatedMessageHistoryTagSummariesView.swift | 18 +- ...idatedMessageHistoryTagsSummaryTable.swift | 135 +++++++++- submodules/Postbox/Sources/Message.swift | 54 +++- .../Postbox/Sources/MessageHistoryTable.swift | 80 ++++-- .../MessageHistoryTagSummaryView.swift | 15 +- .../MessageHistoryTagsSummaryTable.swift | 96 ++++++- .../Sources/MessageHistoryTagsTable.swift | 4 +- .../MessageHistoryThreadIndexView.swift | 2 +- .../Sources/MessageHistoryThreadsTable.swift | 4 +- .../Postbox/Sources/MessageHistoryView.swift | 4 +- .../Sources/MessageHistoryViewState.swift | 4 +- ...MessageHistoryCustomTagSummariesView.swift | 59 +++++ .../PeerThreadCombinedStateTable.swift | 2 +- submodules/Postbox/Sources/Postbox.swift | 33 ++- .../Postbox/Sources/SeedConfiguration.swift | 2 +- submodules/Postbox/Sources/ValueBoxKey.swift | 14 +- submodules/Postbox/Sources/Views.swift | 25 +- .../Sources/ReactionContextNode.swift | 11 +- .../SearchBarNode/Sources/SearchBarNode.swift | 29 +- .../BubbleSettingsController.swift | 10 +- .../ForwardPrivacyChatPreviewItem.swift | 2 +- .../PrivacyAndSecurityController.swift | 83 +++++- .../SelectivePrivacySettingsController.swift | 8 +- .../TextSizeSelectionController.swift | 11 +- .../Themes/ThemePreviewControllerNode.swift | 17 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 4 +- .../TelegramCore/Sources/ForumChannels.swift | 4 +- .../Sources/Settings/PrivacySettings.swift | 12 +- .../State/AccountStateManagementUtils.swift | 10 +- .../Sources/State/AccountViewTracker.swift | 12 +- .../TelegramCore/Sources/State/Holes.swift | 8 +- ...anagedConsumePersonalMessagesActions.swift | 99 ++++++- .../ManagedSecretChatOutgoingOperations.swift | 1 + .../Sources/State/ResetState.swift | 16 +- .../Sources/State/SavedMessageTags.swift | 7 + .../Sources/State/Serialization.swift | 2 +- .../SyncCore_ReactionsMessageAttribute.swift | 99 ++++++- .../TelegramEngine/Data/MessagesData.swift | 60 ++++- .../TelegramEngine/Messages/AdMessages.swift | 1 + .../ApplyMaxReadIndexInteractively.swift | 2 +- ...EarliestUnseenPersonalMentionMessage.swift | 4 +- .../TelegramEngine/Messages/Message.swift | 3 + .../Messages/MessageReadStats.swift | 143 +++++----- .../Messages/TelegramEngineMessages.swift | 9 +- .../Peers/ChatListFiltering.swift | 4 +- .../Privacy/TelegramEnginePrivacy.swift | 4 + .../UpdatedAccountPrivacySettings.swift | 31 ++- .../Stickers/TelegramEngineStickers.swift | 30 ++- .../Sources/Utils/MessageUtils.swift | 4 +- .../ChatMessageDateAndStatusNode.swift | 2 +- .../ChatRecentActionsHistoryTransition.swift | 116 ++++---- .../Sources/ChatEntityKeyboardInputNode.swift | 2 +- .../Sources/PeerInfoPaneNode.swift | 3 + .../PeerInfoScreen/Sources/PeerInfoData.swift | 6 +- .../Sources/PeerInfoHeaderNode.swift | 42 ++- .../Sources/PeerInfoScreen.swift | 17 +- .../Sources/PeerInfoSubtitleBadgeView.swift | 91 +++++++ .../Sources/PeerInfoVisualMediaPaneNode.swift | 2 + .../PeerNameColorChatPreviewItem.swift | 4 +- .../Sources/ReactionChatPreviewItem.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 17 +- .../Sources/WallpaperGalleryItem.swift | 8 +- .../StoryItemSetContainerComponent.swift | 1 + ...StoryItemSetContainerViewSendMessage.swift | 2 +- ...ChatControllerOpenMessageContextMenu.swift | 31 +-- .../TelegramUI/Sources/ChatController.swift | 10 +- .../Sources/ChatControllerNode.swift | 1 + .../Sources/ChatHistoryEntriesForView.swift | 3 + .../Sources/ChatHistoryListNode.swift | 1 + .../ChatInterfaceStateContextMenus.swift | 112 ++++++-- .../ChatSearchNavigationContentNode.swift | 2 +- .../ChatSearchTitleAccessoryPanelNode.swift | 247 +++++++++++++----- 86 files changed, 1660 insertions(+), 478 deletions(-) create mode 100644 submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSubtitleBadgeView.swift diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 0f8919416d..6f94d476d1 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3640,6 +3640,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", @@ -3691,6 +3692,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", @@ -3721,6 +3723,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", @@ -3751,6 +3754,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", @@ -3781,6 +3785,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 5bcc75170c..1d2dea85df 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -174,6 +174,7 @@ public final class ChatListShimmerNode: ASDisplayNode { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: peer1, text: "Text", diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index c93fd1fbb0..c922edd917 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -2252,6 +2252,7 @@ public final class ContextController: ViewController, StandalonePresentableContr public var reactionsTitle: String? public var animationCache: AnimationCache? public var alwaysAllowPremiumReactions: Bool + public var allPresetReactionsAreAvailable: Bool public var getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? public var disablePositionLock: Bool public var tip: Tip? @@ -2267,6 +2268,7 @@ public final class ContextController: ViewController, StandalonePresentableContr reactionsTitle: String? = nil, animationCache: AnimationCache? = nil, alwaysAllowPremiumReactions: Bool = false, + allPresetReactionsAreAvailable: Bool = false, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? = nil, disablePositionLock: Bool = false, tip: Tip? = nil, @@ -2281,6 +2283,7 @@ public final class ContextController: ViewController, StandalonePresentableContr self.selectedReactionItems = selectedReactionItems self.reactionsTitle = reactionsTitle self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions + self.allPresetReactionsAreAvailable = allPresetReactionsAreAvailable self.getEmojiContent = getEmojiContent self.disablePositionLock = disablePositionLock self.tip = tip @@ -2296,6 +2299,7 @@ public final class ContextController: ViewController, StandalonePresentableContr self.selectedReactionItems = Set() self.reactionsTitle = nil self.alwaysAllowPremiumReactions = false + self.allPresetReactionsAreAvailable = false self.getEmojiContent = nil self.disablePositionLock = false self.tip = nil diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 24fa282f17..d5bbc50ddb 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -35,6 +35,28 @@ public protocol ContextControllerActionsStackItemNode: ASDisplayNode { func increaseHighlightedIndex() } +public struct ContextControllerReactionItems { + public var context: AccountContext + public var reactionItems: [ReactionContextItem] + public var selectedReactionItems: Set + public var reactionsTitle: String? + public var animationCache: AnimationCache + public var alwaysAllowPremiumReactions: Bool + public var allPresetReactionsAreAvailable: Bool + public var getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)? + + public init(context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, allPresetReactionsAreAvailable: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?) { + self.context = context + self.reactionItems = reactionItems + self.selectedReactionItems = selectedReactionItems + self.reactionsTitle = reactionsTitle + self.animationCache = animationCache + self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions + self.allPresetReactionsAreAvailable = allPresetReactionsAreAvailable + self.getEmojiContent = getEmojiContent + } +} + public protocol ContextControllerActionsStackItem: AnyObject { func node( getController: @escaping () -> ContextControllerProtocol?, @@ -46,7 +68,7 @@ public protocol ContextControllerActionsStackItem: AnyObject { var id: AnyHashable? { get } var tip: ContextController.Tip? { get } var tipSignal: Signal? { get } - var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { get } + var reactionItems: ContextControllerReactionItems? { get } var dismissed: (() -> Void)? { get } } @@ -911,7 +933,7 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack let id: AnyHashable? let items: [ContextMenuItem] - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: ContextControllerReactionItems? let tip: ContextController.Tip? let tipSignal: Signal? let dismissed: (() -> Void)? @@ -919,7 +941,7 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack init( id: AnyHashable?, items: [ContextMenuItem], - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: ContextControllerReactionItems?, tip: ContextController.Tip?, tipSignal: Signal?, dismissed: (() -> Void)? @@ -1009,7 +1031,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta let id: AnyHashable? private let content: ContextControllerItemsContent - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: ContextControllerReactionItems? let tip: ContextController.Tip? let tipSignal: Signal? let dismissed: (() -> Void)? @@ -1017,7 +1039,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta init( id: AnyHashable?, content: ContextControllerItemsContent, - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: ContextControllerReactionItems?, tip: ContextController.Tip?, tipSignal: Signal?, dismissed: (() -> Void)? @@ -1046,9 +1068,18 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta } func makeContextControllerActionsStackItem(items: ContextController.Items) -> [ContextControllerActionsStackItem] { - var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + var reactionItems: ContextControllerReactionItems? if let context = items.context, let animationCache = items.animationCache, !items.reactionItems.isEmpty { - reactionItems = (context, items.reactionItems, items.selectedReactionItems, reactionsTitle: items.reactionsTitle, animationCache, alwaysAllowPremiumReactions: items.alwaysAllowPremiumReactions, items.getEmojiContent) + reactionItems = ContextControllerReactionItems( + context: context, + reactionItems: items.reactionItems, + selectedReactionItems: items.selectedReactionItems, + reactionsTitle: items.reactionsTitle, + animationCache: animationCache, + alwaysAllowPremiumReactions: items.alwaysAllowPremiumReactions, + allPresetReactionsAreAvailable: items.allPresetReactionsAreAvailable, + getEmojiContent: items.getEmojiContent + ) } switch items.content { case let .list(listItems): @@ -1172,7 +1203,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { var tip: ContextController.Tip? let tipSignal: Signal? var tipNode: InnerTextSelectionTipContainerNode? - let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? + let reactionItems: ContextControllerReactionItems? let itemDismissed: (() -> Void)? var storedScrollingState: CGFloat? let positionLock: CGFloat? @@ -1187,7 +1218,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { item: ContextControllerActionsStackItem, tip: ContextController.Tip?, tipSignal: Signal?, - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)?, + reactionItems: ContextControllerReactionItems?, itemDismissed: (() -> Void)?, positionLock: CGFloat? ) { @@ -1338,7 +1369,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { private var selectionPanGesture: UIPanGestureRecognizer? - var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set, reactionsTitle: String?, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?)? { + var topReactionItems: ContextControllerReactionItems? { return self.itemContainers.last?.reactionItems } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index fbeb50e9f8..12f32c991f 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -650,6 +650,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo selectedItems: reactionItems.selectedReactionItems, title: reactionItems.reactionsTitle, alwaysAllowPremiumReactions: reactionItems.alwaysAllowPremiumReactions, + allPresetReactionsAreAvailable: reactionItems.allPresetReactionsAreAvailable, getEmojiContent: reactionItems.getEmojiContent, isExpandedUpdated: { [weak self] transition in guard let strongSelf = self else { diff --git a/submodules/DrawingUI/Sources/DrawingReactionView.swift b/submodules/DrawingUI/Sources/DrawingReactionView.swift index 816f7b3ec1..70fc7eeef4 100644 --- a/submodules/DrawingUI/Sources/DrawingReactionView.swift +++ b/submodules/DrawingUI/Sources/DrawingReactionView.swift @@ -137,6 +137,7 @@ public class DrawingReactionEntityView: DrawingStickerEntityView { selectedItems: Set(), title: nil, alwaysAllowPremiumReactions: false, + allPresetReactionsAreAvailable: false, getEmojiContent: { [weak self] animationCache, animationRenderer in guard let self else { preconditionFailure() diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index b10ac864ab..b3f2ebcbbb 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -725,7 +725,7 @@ public class StickerPickerScreen: ViewController { } let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) - let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let gallery = GalleryController(context: context, source: .standaloneMessage(message), streamSingleVideo: true, replaceRootController: { _, _ in }, baseNavigationController: nil) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 885bc1ae4f..6489e5cce0 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1427,7 +1427,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { }, showAll: false) let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) - let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let controller = LocationViewController(context: self.context, subject: EngineMessage(message), params: controllerParams) self.pushController(controller) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index f48f1b2b86..33cc6d4df6 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -858,10 +858,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview - let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true) - let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true) let headerItems: [ListViewItem] = [previewItem, dragItem] diff --git a/submodules/Postbox/Sources/ChatListTable.swift b/submodules/Postbox/Sources/ChatListTable.swift index cfd1cc4a5f..0ac53e448a 100644 --- a/submodules/Postbox/Sources/ChatListTable.swift +++ b/submodules/Postbox/Sources/ChatListTable.swift @@ -979,7 +979,7 @@ final class ChatListTable: Table { } var tagSummary: MessageHistoryTagNamespaceSummary? if let summaryTag = summaryTag { - tagSummary = summaryTable.get(MessageHistoryTagsSummaryKey(tag: summaryTag, peerId: peerIndex.messageIndex.id.peerId, threadId: nil, namespace: namespace)) + tagSummary = summaryTable.get(MessageHistoryTagsSummaryKey(tag: summaryTag, peerId: peerIndex.messageIndex.id.peerId, threadId: nil, namespace: namespace, customTag: nil)) } var topMessageAttributes: [MessageAttribute] = [] if let topMessage = topMessage { diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 35d3d44dbe..2f53096d26 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -110,7 +110,7 @@ private func updateMessagePeers(_ message: Message, updatedPeers: [PeerId: Peer] peers[peerId] = currentPeer } } - return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) + return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) } return nil } @@ -790,7 +790,7 @@ private final class ChatListViewSpaceState { return nil } - let updatedMessageSummary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: filterMessageTagSummary.addCount.tag, peerId: entryPeer.id, threadId: nil, namespace: filterMessageTagSummary.addCount.namespace)] + let updatedMessageSummary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: filterMessageTagSummary.addCount.tag, peerId: entryPeer.id, threadId: nil, namespace: filterMessageTagSummary.addCount.namespace, customTag: nil)] let updatedActionsSummary = transaction.currentUpdatedMessageActionsSummaries[PendingMessageActionsSummaryKey(type: filterMessageTagSummary.subtractCount.type, peerId: entryPeer.id, namespace: filterMessageTagSummary.subtractCount.namespace)] if updatedMessageSummary != nil || updatedActionsSummary != nil { @@ -923,7 +923,7 @@ private final class ChatListViewSpaceState { for (key, component) in self.summaryComponents.components { var updatedTagSummaryCount: Int32? if let tagSummary = component.tagSummary { - let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: entryData.index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace) + let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: entryData.index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace, customTag: nil) if let summary = transaction.currentUpdatedMessageTagSummaries[key] { updatedTagSummaryCount = summary.count } @@ -1534,7 +1534,7 @@ struct ChatListViewState { var actionsSummaryCount: Int32? if let tagSummary = component.tagSummary { - let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace) + let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace, customTag: nil) if let summary = postbox.messageHistoryTagsSummaryTable.get(key) { tagSummaryCount = summary.count } diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index bed9fefa57..12299e8974 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -51,7 +51,7 @@ public func persistentHash32(_ string: String) -> Int32 { private let emptyMemory = malloc(1)! -public class MemoryBuffer: Equatable, CustomStringConvertible { +public class MemoryBuffer: Comparable, Hashable, CustomStringConvertible { public internal(set) var memory: UnsafeMutableRawPointer var capacity: Int public internal(set) var length: Int @@ -122,9 +122,25 @@ public class MemoryBuffer: Equatable, CustomStringConvertible { f(Data(bytesNoCopy: self.memory, count: self.length, deallocator: .none)) } + public func hash(into hasher: inout Hasher) { + if self.length == 0 { + hasher.combine(0 as Int) + } else { + hasher.combine(bytes: UnsafeRawBufferPointer(start: self.memory, count: self.length)) + } + } + + public func withRawBufferPointer(_ f: (UnsafeRawBufferPointer) -> Void) { + f(UnsafeRawBufferPointer(start: self.memory, count: self.length)) + } + public static func ==(lhs: MemoryBuffer, rhs: MemoryBuffer) -> Bool { return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0 } + + public static func <(lhs: MemoryBuffer, rhs: MemoryBuffer) -> Bool { + return mdb_cmp_memn(lhs.memory, lhs.length, rhs.memory, rhs.length) < 0 + } } public final class WriteBuffer: MemoryBuffer { @@ -134,6 +150,10 @@ public final class WriteBuffer: MemoryBuffer { super.init(memory: malloc(32), capacity: 32, length: 0, freeWhenDone: true) } + public init(capacity: Int) { + super.init(memory: malloc(capacity), capacity: capacity, length: 0, freeWhenDone: true) + } + public func makeReadBufferAndReset() -> ReadBuffer { let buffer = ReadBuffer(memory: self.memory, length: self.offset, freeWhenDone: true) self.memory = malloc(32) @@ -222,6 +242,12 @@ public final class ReadBuffer: MemoryBuffer { return result } + public func readMemoryBuffer(length: Int) -> MemoryBuffer { + let result = MemoryBuffer(memory: malloc(length)!, capacity: length, length: length, freeWhenDone: true) + self.read(result.memory, offset: 0, length: length) + return result + } + public func skip(_ length: Int) { self.offset += length } diff --git a/submodules/Postbox/Sources/GlobalMessageTagsView.swift b/submodules/Postbox/Sources/GlobalMessageTagsView.swift index 129d58665a..ce4c10763d 100644 --- a/submodules/Postbox/Sources/GlobalMessageTagsView.swift +++ b/submodules/Postbox/Sources/GlobalMessageTagsView.swift @@ -163,11 +163,11 @@ final class MutableGlobalMessageTagsView: MutablePostboxView { hasChanges = true } case let .intermediateMessage(message): - if self.add(.intermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia))) { + if self.add(.intermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia))) { hasChanges = true } case let .message(message): - if self.add(.message(Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories))) { + if self.add(.message(Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories))) { hasChanges = true } } diff --git a/submodules/Postbox/Sources/IntermediateMessage.swift b/submodules/Postbox/Sources/IntermediateMessage.swift index d326376868..f68f90129f 100644 --- a/submodules/Postbox/Sources/IntermediateMessage.swift +++ b/submodules/Postbox/Sources/IntermediateMessage.swift @@ -43,6 +43,7 @@ class IntermediateMessage { let tags: MessageTags let globalTags: GlobalMessageTags let localTags: LocalMessageTags + let customTags: [MemoryBuffer] let forwardInfo: IntermediateMessageForwardInfo? let authorId: PeerId? let text: String @@ -54,7 +55,7 @@ class IntermediateMessage { return MessageIndex(id: self.id, timestamp: self.timestamp) } - init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: IntermediateMessageForwardInfo?, authorId: PeerId?, text: String, attributesData: ReadBuffer, embeddedMediaData: ReadBuffer, referencedMedia: [MediaId]) { + init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, customTags: [MemoryBuffer], forwardInfo: IntermediateMessageForwardInfo?, authorId: PeerId?, text: String, attributesData: ReadBuffer, embeddedMediaData: ReadBuffer, referencedMedia: [MediaId]) { self.stableId = stableId self.stableVersion = stableVersion self.id = id @@ -67,6 +68,7 @@ class IntermediateMessage { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTags self.forwardInfo = forwardInfo self.authorId = authorId self.text = text @@ -76,18 +78,18 @@ class IntermediateMessage { } func withUpdatedTimestamp(_ timestamp: Int32) -> IntermediateMessage { - return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) + return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) } func withUpdatedGroupingKey(_ groupingKey: Int64?) -> IntermediateMessage { - return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) + return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) } func withUpdatedGroupInfo(_ groupInfo: MessageGroupInfo?) -> IntermediateMessage { - return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) + return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: self.embeddedMediaData, referencedMedia: self.referencedMedia) } func withUpdatedEmbeddedMedia(_ embeddedMedia: ReadBuffer) -> IntermediateMessage { - return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: embeddedMedia, referencedMedia: self.referencedMedia) + return IntermediateMessage(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributesData: self.attributesData, embeddedMediaData: embeddedMedia, referencedMedia: self.referencedMedia) } } diff --git a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift index adb313a021..702ad59eab 100644 --- a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift +++ b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagSummariesView.swift @@ -1,3 +1,4 @@ +import Foundation final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView { private let peerId: PeerId? @@ -13,10 +14,13 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView self.tagMask = tagMask self.namespace = namespace + self.reload(postbox: postbox) + } + + private func reload(postbox: PostboxImpl) { if let peerId = self.peerId { - if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) { - self.entries.insert(entry) - } + self.entries.removeAll() + self.entries.formUnion(postbox.invalidatedMessageHistoryTagsSummaryTable.getIncludingCustomTags(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace)) } else { for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) { self.entries.insert(entry) @@ -45,21 +49,19 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView } if maybeUpdated { self.entries.removeAll() - if let entry = postbox.invalidatedMessageHistoryTagsSummaryTable.get(peerId: peerId, threadId: self.threadId, tagMask: self.tagMask, namespace: self.namespace) { - self.entries.insert(entry) - } + self.reload(postbox: postbox) updated = true } } else { for operation in transaction.currentInvalidateMessageTagSummaries { switch operation { case let .add(entry): - if entry.key.namespace == self.namespace && entry.key.tagMask == self.tagMask { + if entry.key.namespace == self.namespace && entry.key.tagMask == self.tagMask && entry.key.customTag == nil { self.entries.insert(entry) updated = true } case let .remove(key): - if key.namespace == self.namespace && key.tagMask == self.tagMask { + if key.namespace == self.namespace && key.tagMask == self.tagMask && key.customTag == nil { for entry in self.entries { if entry.key == key { self.entries.remove(entry) diff --git a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagsSummaryTable.swift b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagsSummaryTable.swift index 6ffa3d062f..e97da47e15 100644 --- a/submodules/Postbox/Sources/InvalidatedMessageHistoryTagsSummaryTable.swift +++ b/submodules/Postbox/Sources/InvalidatedMessageHistoryTagsSummaryTable.swift @@ -1,22 +1,64 @@ import Foundation -public struct InvalidatedMessageHistoryTagsSummaryKey: Equatable, Hashable { +public struct InvalidatedMessageHistoryTagsSummaryKey: Comparable, Hashable { public let peerId: PeerId public let namespace: MessageId.Namespace public let tagMask: MessageTags public let threadId: Int64? + public let customTag: MemoryBuffer? - public init(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags, threadId: Int64?) { + public init(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags, threadId: Int64?, customTag: MemoryBuffer?) { self.peerId = peerId self.namespace = namespace self.tagMask = tagMask self.threadId = threadId + self.customTag = customTag + } + + public static func <(lhs: InvalidatedMessageHistoryTagsSummaryKey, rhs: InvalidatedMessageHistoryTagsSummaryKey) -> Bool { + if lhs.peerId != rhs.peerId { + return lhs.peerId < rhs.peerId + } + if lhs.namespace != rhs.namespace { + return lhs.namespace != rhs.namespace + } + if lhs.tagMask != rhs.tagMask { + return lhs.tagMask.rawValue < rhs.tagMask.rawValue + } + if let lhsThreadId = lhs.threadId, let rhsThreadId = rhs.threadId { + if lhsThreadId != rhsThreadId { + return lhsThreadId < rhsThreadId + } + } else if (lhs.threadId == nil) != (rhs.threadId == nil) { + if lhs.threadId != nil { + return true + } else { + return false + } + } + if let lhsCustomTag = lhs.customTag, let rhsCustomTag = rhs.customTag { + if lhsCustomTag != rhsCustomTag { + return lhsCustomTag < rhsCustomTag + } + } else if (lhs.customTag == nil) != (rhs.customTag == nil) { + if lhs.customTag != nil { + return true + } else { + return false + } + } + return false } } public struct InvalidatedMessageHistoryTagsSummaryEntry: Equatable, Hashable { public let key: InvalidatedMessageHistoryTagsSummaryKey public let version: Int32 + + public init(key: InvalidatedMessageHistoryTagsSummaryKey, version: Int32) { + self.key = key + self.version = version + } } enum InvalidatedMessageHistoryTagsSummaryEntryOperation { @@ -30,7 +72,34 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table { } private func key(_ key: InvalidatedMessageHistoryTagsSummaryKey) -> ValueBoxKey { - if let threadId = key.threadId { + if let customTag = key.customTag, customTag.length != 0 { + var keyLength = 4 + 4 + 8 + keyLength += 8 + keyLength += customTag.length + let result = ValueBoxKey(length: keyLength) + + var offset = 0 + result.setUInt32(offset, value: key.tagMask.rawValue) + offset += 4 + + result.setInt32(offset, value: key.namespace) + offset += 4 + + result.setInt64(offset, value: key.peerId.toInt64()) + offset += 8 + + result.setInt64(offset, value: key.threadId ?? 0) + offset += 8 + + if customTag.length != 0 { + customTag.withRawBufferPointer { buffer in + result.setBytes(offset, value: buffer) + offset += buffer.count + } + } + + return result + } else if let threadId = key.threadId { let result = ValueBoxKey(length: 4 + 4 + 8 + 8) result.setUInt32(0, value: key.tagMask.rawValue) result.setInt32(4, value: key.namespace) @@ -64,18 +133,65 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table { value.read(&version, offset: 0, length: 4) var threadId: Int64? + var customTag: MemoryBuffer? if key.length >= 4 + 4 + 8 + 8 { threadId = key.getInt64(4 + 4 + 8) + + if key.length > 4 + 4 + 8 + 8 { + customTag = key.getMemoryBuffer(4 + 4 + 8 + 8, length: key.length - (4 + 4 + 8 + 8)) + if threadId == 0 { + threadId = nil + } + } } - entries.append(InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(4), tagMask: MessageTags(rawValue: key.getUInt32(0)), threadId: threadId), version: version)) + entries.append(InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(4), tagMask: MessageTags(rawValue: key.getUInt32(0)), threadId: threadId, customTag: customTag), version: version)) return true }, limit: 0) return entries } - func get(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> InvalidatedMessageHistoryTagsSummaryEntry? { - return self.get(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId)) + func get(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, customTag: MemoryBuffer?) -> InvalidatedMessageHistoryTagsSummaryEntry? { + return self.get(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId, customTag: customTag)) + } + + func getIncludingCustomTags(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> [InvalidatedMessageHistoryTagsSummaryEntry] { + var entries: [InvalidatedMessageHistoryTagsSummaryEntry] = [] + + let peerKey = self.key(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId, customTag: nil)) + self.valueBox.range( + self.table, + start: peerKey.predecessor, end: peerKey.successor, values: { key, value in + var version: Int32 = 0 + value.read(&version, offset: 0, length: 4) + + var threadId: Int64? + var customTag: MemoryBuffer? + if key.length >= 4 + 4 + 8 + 8 { + threadId = key.getInt64(4 + 4 + 8) + + if key.length > 4 + 4 + 8 + 8 { + customTag = key.getMemoryBuffer(4 + 4 + 8 + 8, length: key.length - (4 + 4 + 8 + 8)) + if threadId == 0 { + threadId = nil + } + } + } + + let entry = InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(4), tagMask: MessageTags(rawValue: key.getUInt32(0)), threadId: threadId, customTag: customTag), version: version) + assert(entry.key.peerId == peerId) + assert(entry.key.namespace == namespace) + assert(entry.key.tagMask == tagMask) + assert(entry.key.threadId == threadId) + + entries.append(entry) + + return true + }, + limit: 0 + ) + + return entries } private func get(_ key: InvalidatedMessageHistoryTagsSummaryKey) -> InvalidatedMessageHistoryTagsSummaryEntry? { @@ -105,6 +221,13 @@ final class InvalidatedMessageHistoryTagsSummaryTable: Table { } } + func removeEntriesWithCustomTags(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags, operations: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) { + for entry in self.getIncludingCustomTags(peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace) { + self.valueBox.remove(self.table, key: self.key(entry.key), secure: false) + operations.append(.remove(entry.key)) + } + } + override func clearMemoryCache() { } diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index af43f4f21b..50617ebe15 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -613,6 +613,7 @@ public protocol MessageAttribute: AnyObject, PostboxCoding { var associatedMediaIds: [MediaId] { get } var automaticTimestampBasedAttribute: (UInt16, Int32)? { get } var associatedStoryIds: [StoryId] { get } + var customTags: [MemoryBuffer] { get } } public extension MessageAttribute { @@ -635,6 +636,10 @@ public extension MessageAttribute { var associatedStoryIds: [StoryId] { return [] } + + var customTags: [MemoryBuffer] { + return [] + } } public struct MessageGroupInfo: Equatable { @@ -688,6 +693,7 @@ public final class Message { public let tags: MessageTags public let globalTags: GlobalMessageTags public let localTags: LocalMessageTags + public let customTags: [MemoryBuffer] public let forwardInfo: MessageForwardInfo? public let author: Peer? public let text: String @@ -704,7 +710,7 @@ public final class Message { return MessageIndex(id: self.id, timestamp: self.timestamp) } - public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary, associatedMessages: SimpleDictionary, associatedMessageIds: [MessageId], associatedMedia: [MediaId: Media], associatedThreadInfo: AssociatedThreadInfo?, associatedStories: [StoryId: CodableEntry]) { + public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, customTags: [MemoryBuffer], forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary, associatedMessages: SimpleDictionary, associatedMessageIds: [MessageId], associatedMedia: [MediaId: Media], associatedThreadInfo: AssociatedThreadInfo?, associatedStories: [StoryId: CodableEntry]) { self.stableId = stableId self.stableVersion = stableVersion self.id = id @@ -717,6 +723,7 @@ public final class Message { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTags self.forwardInfo = forwardInfo self.author = author self.text = text @@ -731,47 +738,47 @@ public final class Message { } public func withUpdatedStableVersion(stableVersion: UInt32) -> Message { - return Message(stableId: self.stableId, stableVersion: stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedText(_ text: String) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedTimestamp(_ timestamp: Int32) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedMedia(_ media: [Media]) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedPeers(_ peers: SimpleDictionary) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedFlags(_ flags: MessageFlags) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } func withUpdatedGroupInfo(_ groupInfo: MessageGroupInfo?) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedAttributes(_ attributes: [MessageAttribute]) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } func withUpdatedAssociatedMessages(_ associatedMessages: SimpleDictionary) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedForwardInfo(_ forwardInfo: MessageForwardInfo?) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } public func withUpdatedAuthor(_ author: Peer?) -> Message { - return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, customTags: self.customTags, forwardInfo: self.forwardInfo, author: author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo, associatedStories: self.associatedStories) } } @@ -879,6 +886,21 @@ public struct MessageThreadKey: Hashable { } } +private func customTagsFromAttributes(_ attributes: [MessageAttribute]) -> [MemoryBuffer] { + var result: [MemoryBuffer] = [] + for attribute in attributes { + for customTag in attribute.customTags { + if !result.contains(customTag) { + result.append(customTag) + } + } + } + if !result.isEmpty { + result.sort() + } + return result +} + public final class StoreMessage { public let id: StoreMessageId public let timestamp: Int32 @@ -889,6 +911,7 @@ public final class StoreMessage { public let tags: MessageTags public let globalTags: GlobalMessageTags public let localTags: LocalMessageTags + public let customTags: [MemoryBuffer] public let forwardInfo: StoreMessageForwardInfo? public let authorId: PeerId? public let text: String @@ -905,6 +928,7 @@ public final class StoreMessage { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTagsFromAttributes(attributes) self.forwardInfo = forwardInfo self.authorId = authorId self.text = text @@ -922,6 +946,7 @@ public final class StoreMessage { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTagsFromAttributes(attributes) self.forwardInfo = forwardInfo self.authorId = authorId self.text = text @@ -939,6 +964,7 @@ public final class StoreMessage { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTagsFromAttributes(attributes) self.forwardInfo = forwardInfo self.authorId = authorId self.text = text @@ -985,6 +1011,7 @@ final class InternalStoreMessage { let tags: MessageTags let globalTags: GlobalMessageTags let localTags: LocalMessageTags + let customTags: [MemoryBuffer] let forwardInfo: StoreMessageForwardInfo? let authorId: PeerId? let text: String @@ -995,7 +1022,7 @@ final class InternalStoreMessage { return MessageIndex(id: self.id, timestamp: self.timestamp) } - init(id: MessageId, timestamp: Int32, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + init(id: MessageId, timestamp: Int32, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, customTags: [MemoryBuffer], forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = id self.timestamp = timestamp self.globallyUniqueId = globallyUniqueId @@ -1005,6 +1032,7 @@ final class InternalStoreMessage { self.tags = tags self.globalTags = globalTags self.localTags = localTags + self.customTags = customTags self.forwardInfo = forwardInfo self.authorId = authorId self.text = text diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 3a69f7c5f7..dbf004e2da 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -289,8 +289,13 @@ final class MessageHistoryTable: Table { if let threadId = message.threadId { self.threadsTable.add(threadId: threadId, index: message.index) - self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: nil), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } + + for customTag in message.customTags { + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } + let globalTags = message.globalTags.rawValue if globalTags != 0 { for i in 0 ..< 32 { @@ -401,11 +406,11 @@ final class MessageHistoryTable: Table { var internalStoreMessages: [InternalStoreMessage] = [] for message in messages { switch message.id { - case let .Id(id): - internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) - case let .Partial(peerId, namespace): - let id = self.historyMetadataTable.getNextMessageIdAndIncrement(peerId, namespace: namespace) - internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + case let .Id(id): + internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + case let .Partial(peerId, namespace): + let id = self.historyMetadataTable.getNextMessageIdAndIncrement(peerId, namespace: namespace) + internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) } } return internalStoreMessages @@ -1270,9 +1275,19 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&idId, offset: 0, length: 8) } + var customTagCount: Int32 = Int32(message.customTags.count) + sharedBuffer.write(&customTagCount, offset: 0, length: 4) + for customTag in message.customTags { + var customTagLength: Int32 = Int32(customTag.length) + sharedBuffer.write(&customTagLength, offset: 0, length: 4) + customTag.withRawBufferPointer { buffer in + sharedBuffer.write(buffer.baseAddress!, length: buffer.count) + } + } + self.valueBox.set(self.table, key: self.key(message.index, key: sharedKey), value: sharedBuffer) - let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), updatedGroupInfos) + let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), updatedGroupInfos) return result } @@ -1355,7 +1370,10 @@ final class MessageHistoryTable: Table { if let threadId = message.threadId { self.threadsTable.remove(threadId: threadId, index: index) - self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: nil), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } + for customTag in message.customTags { + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } for tag in message.globalTags { self.globalTagsTable.remove(tag, index: index) @@ -1456,7 +1474,7 @@ final class MessageHistoryTable: Table { } withExtendedLifetime(updatedEmbeddedMediaBuffer, { - self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: message.referencedMedia), sharedKey: self.key(index)) + self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: message.referencedMedia), sharedKey: self.key(index)) }) let operation: MessageHistoryOperation = .UpdateEmbeddedMedia(index, updatedEmbeddedMediaBuffer.makeReadBufferAndReset()) @@ -1574,13 +1592,22 @@ final class MessageHistoryTable: Table { if let threadId = previousMessage.threadId { self.threadsTable.remove(threadId: threadId, index: index) - self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, customTag: nil), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } if let threadId = message.threadId { self.threadsTable.add(threadId: threadId, index: message.index) - self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: threadId, namespace: message.id.namespace, customTag: nil), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } + } + + if previousMessage.customTags != message.customTags || index != message.index { + for customTag in previousMessage.customTags { + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace, customTag: customTag), id: index.id.id, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) + } + for customTag in message.customTags { + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: MessageTags(), peerId: message.id.peerId, threadId: nil, namespace: message.id.namespace, customTag: customTag), id: message.id.id, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) } } @@ -1919,9 +1946,19 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&idId, offset: 0, length: 8) } + var customTagCount: Int32 = Int32(message.customTags.count) + sharedBuffer.write(&customTagCount, offset: 0, length: 4) + for customTag in message.customTags { + var customTagLength: Int32 = Int32(customTag.length) + sharedBuffer.write(&customTagLength, offset: 0, length: 4) + customTag.withRawBufferPointer { buffer in + sharedBuffer.write(buffer.baseAddress!, length: buffer.count) + } + } + self.valueBox.set(self.table, key: self.key(message.index, key: sharedKey), value: sharedBuffer) - let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags) + let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags) for media in mediaToUpdate { if let id = media.id { @@ -1999,7 +2036,7 @@ final class MessageHistoryTable: Table { let updatedIndex = MessageIndex(id: index.id, timestamp: timestamp) - let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) + let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, customTags: previousMessage.customTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) return (previousMessage.tags, previousMessage.globalTags) } else { return nil @@ -2039,7 +2076,7 @@ final class MessageHistoryTable: Table { var updatedReferencedMedia = message.referencedMedia updatedReferencedMedia.append(extractedMedia.id!) withExtendedLifetime(updatedEmbeddedMediaBuffer, { - self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: updatedReferencedMedia), sharedKey: self.key(index)) + self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: updatedReferencedMedia), sharedKey: self.key(index)) }) return extractedMedia @@ -2453,7 +2490,18 @@ final class MessageHistoryTable: Table { referencedMediaIds.append(MediaId(namespace: idNamespace, id: idId)) } - return IntermediateMessageHistoryEntry(message: IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: index.id, globallyUniqueId: globallyUniqueId, groupingKey: groupingKey, groupInfo: groupInfo, threadId: threadId, timestamp: index.timestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: forwardInfo, authorId: authorId, text: text, attributesData: attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: referencedMediaIds)) + var customTags: [MemoryBuffer] = [] + var customTagCount: Int32 = 0 + if value.offset < value.length { + value.read(&customTagCount, offset: 0, length: 4) + for _ in 0 ..< customTagCount { + var customTagLength: Int32 = 0 + value.read(&customTagLength, offset: 0, length: 4) + customTags.append(value.readMemoryBuffer(length: Int(customTagLength))) + } + } + + return IntermediateMessageHistoryEntry(message: IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: index.id, globallyUniqueId: globallyUniqueId, groupingKey: groupingKey, groupInfo: groupInfo, threadId: threadId, timestamp: index.timestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, customTags: customTags, forwardInfo: forwardInfo, authorId: authorId, text: text, attributesData: attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: referencedMediaIds)) } else { preconditionFailure() } @@ -2639,7 +2687,7 @@ final class MessageHistoryTable: Table { associatedThreadInfo = self.seedConfiguration.decodeMessageThreadInfo(data.data) } - return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds, associatedMedia: associatedMedia, associatedThreadInfo: associatedThreadInfo, associatedStories: associatedStories) + return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds, associatedMedia: associatedMedia, associatedThreadInfo: associatedThreadInfo, associatedStories: associatedStories) } func renderMessagePeers(_ message: Message, peerTable: PeerTable) -> Message { diff --git a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift index be8d0c6dba..85156867e4 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagSummaryView.swift @@ -5,22 +5,24 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView { private let peerId: PeerId private let threadId: Int64? private let namespace: MessageId.Namespace + private let customTag: MemoryBuffer? fileprivate var count: Int32? - init(postbox: PostboxImpl, tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) { + init(postbox: PostboxImpl, tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, customTag: MemoryBuffer?) { self.tag = tag self.peerId = peerId self.threadId = threadId self.namespace = namespace + self.customTag = customTag - self.count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: threadId, namespace: namespace))?.count + self.count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: threadId, namespace: namespace, customTag: customTag))?.count } func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { var hasChanges = false - if let summary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: self.namespace)] { + if let summary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: self.namespace, customTag: self.customTag)] { self.count = summary.count hasChanges = true } @@ -29,13 +31,6 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - /*let count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace))?.count - if self.count != count { - self.count = count - return true - } else { - return false - }*/ return false } diff --git a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift index 9d240a86e0..e1148ef0d5 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagsSummaryTable.swift @@ -45,6 +45,15 @@ struct MessageHistoryTagsSummaryKey: Equatable, Hashable { let peerId: PeerId let threadId: Int64? let namespace: MessageId.Namespace + let customTag: MemoryBuffer? + + init(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, customTag: MemoryBuffer?) { + self.tag = tag + self.peerId = peerId + self.threadId = threadId + self.namespace = namespace + self.customTag = customTag + } } private func readSummary(_ value: ReadBuffer) -> MessageHistoryTagNamespaceSummary { @@ -91,17 +100,62 @@ class MessageHistoryTagsSummaryTable: Table { } private func keyShared(key: MessageHistoryTagsSummaryKey) -> ValueBoxKey { - if let threadId = key.threadId { - self.sharedThreadKey.setUInt32(0, value: key.tag.rawValue) - self.sharedThreadKey.setInt64(4, value: key.peerId.toInt64()) - self.sharedThreadKey.setInt32(4 + 8, value: key.namespace) - self.sharedThreadKey.setInt64(4 + 8 + 4, value: threadId) - return self.sharedThreadKey + return self.keyInternal(key: key, allowShared: true) + } + + private func keyInternal(key: MessageHistoryTagsSummaryKey, allowShared: Bool) -> ValueBoxKey { + if let customTag = key.customTag { + if customTag.length != 10 && customTag.length != 7 { + assert(true) + } + + var keyLength = 4 + 8 + 4 + keyLength += 8 + keyLength += customTag.length + let result = ValueBoxKey(length: keyLength) + + var offset = 0 + result.setUInt32(offset, value: key.tag.rawValue) + offset += 4 + + result.setInt64(offset, value: key.peerId.toInt64()) + offset += 8 + + result.setInt32(offset, value: key.namespace) + offset += 4 + + result.setInt64(offset, value: key.threadId ?? 0) + offset += 8 + + customTag.withRawBufferPointer { buffer in + result.setBytes(offset, value: buffer) + offset += buffer.count + } + + return result + } else if let threadId = key.threadId { + let result: ValueBoxKey + if allowShared { + result = self.sharedThreadKey + } else { + result = ValueBoxKey(length: 4 + 8 + 4 + 8) + } + result.setUInt32(0, value: key.tag.rawValue) + result.setInt64(4, value: key.peerId.toInt64()) + result.setInt32(4 + 8, value: key.namespace) + result.setInt64(4 + 8 + 4, value: threadId) + return result } else { - self.sharedSimpleKey.setUInt32(0, value: key.tag.rawValue) - self.sharedSimpleKey.setInt64(4, value: key.peerId.toInt64()) - self.sharedSimpleKey.setInt32(4 + 8, value: key.namespace) - return self.sharedSimpleKey + let result: ValueBoxKey + if allowShared { + result = self.sharedSimpleKey + } else { + result = ValueBoxKey(length: 4 + 8 + 4) + } + result.setUInt32(0, value: key.tag.rawValue) + result.setInt64(4, value: key.peerId.toInt64()) + result.setInt32(4 + 8, value: key.namespace) + return result } } @@ -118,6 +172,24 @@ class MessageHistoryTagsSummaryTable: Table { } } + func getCustomTags(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) -> [MemoryBuffer] { + let peerKey = self.keyInternal(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: threadId, namespace: namespace, customTag: nil), allowShared: false) + let prefixLength = 4 + 8 + 4 + 8 + var result: [MemoryBuffer] = [] + self.valueBox.range(self.table, start: peerKey.predecessor, end: peerKey.successor, keys: { key in + let testPeerId = key.getInt64(4) + assert(PeerId(testPeerId) == peerId) + let testNamespace = key.getInt32(4 + 8) + assert(testNamespace == namespace) + + if key.length > prefixLength { + result.append(key.getMemoryBuffer(prefixLength, length: key.length - prefixLength)) + } + return true + }, limit: 0) + return result + } + private func set(_ key: MessageHistoryTagsSummaryKey, summary: MessageHistoryTagNamespaceSummary, updatedSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary]) { if self.get(key) != summary { if key.tag.rawValue == 2048 { @@ -134,12 +206,12 @@ class MessageHistoryTagsSummaryTable: Table { if !isNewlyAdded || !current.range.contains(id) { self.set(key, summary: current.withAddedCount(1), updatedSummaries: &updatedSummaries) if current.range.maxId == 0 { - self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId), operations: &invalidateSummaries) + self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId, customTag: key.customTag), operations: &invalidateSummaries) } } } else { self.set(key, summary: MessageHistoryTagNamespaceSummary(version: 0, count: 1, range: MessageHistoryTagNamespaceCountValidityRange(maxId: 0)), updatedSummaries: &updatedSummaries) - self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId), operations: &invalidateSummaries) + self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag, threadId: key.threadId, customTag: key.customTag), operations: &invalidateSummaries) } } diff --git a/submodules/Postbox/Sources/MessageHistoryTagsTable.swift b/submodules/Postbox/Sources/MessageHistoryTagsTable.swift index 6e1a9fc564..8470337d41 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagsTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagsTable.swift @@ -46,7 +46,7 @@ class MessageHistoryTagsTable: Table { for tag in tags { self.valueBox.set(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), value: MemoryBuffer()) if self.summaryTags.contains(tag) { - self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace, customTag: nil), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) } } } @@ -56,7 +56,7 @@ class MessageHistoryTagsTable: Table { self.valueBox.remove(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), secure: false) if self.summaryTags.contains(tag) { - self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace, customTag: nil), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) } } } diff --git a/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift b/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift index d40bd2002a..cb8976110b 100644 --- a/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift +++ b/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift @@ -79,7 +79,7 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView { var actionsSummaryCount: Int32? if let tagSummary = component.tagSummary { - let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: self.peerId, threadId: item.threadId, namespace: tagSummary.namespace) + let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: self.peerId, threadId: item.threadId, namespace: tagSummary.namespace, customTag: nil) if let summary = postbox.messageHistoryTagsSummaryTable.get(key) { tagSummaryCount = summary.count } diff --git a/submodules/Postbox/Sources/MessageHistoryThreadsTable.swift b/submodules/Postbox/Sources/MessageHistoryThreadsTable.swift index dcc686dc7b..df2f5ae342 100644 --- a/submodules/Postbox/Sources/MessageHistoryThreadsTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryThreadsTable.swift @@ -229,7 +229,7 @@ class MessageHistoryThreadTagsTable: Table { for tag in tags { self.valueBox.set(self.table, key: self.key(tag: tag, threadId: threadId, index: index, key: self.sharedKey), value: MemoryBuffer()) if self.summaryTags.contains(tag) { - self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) + self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, customTag: nil), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) } } } @@ -238,7 +238,7 @@ class MessageHistoryThreadTagsTable: Table { for tag in tags { self.valueBox.remove(self.table, key: self.key(tag: tag, threadId: threadId, index: index, key: self.sharedKey), secure: false) if self.summaryTags.contains(tag) { - self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) + self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, customTag: nil), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) } } } diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 4611261d2e..4220307900 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -133,11 +133,11 @@ enum MutableMessageHistoryEntry { func updatedTimestamp(_ timestamp: Int32) -> MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, location, monthLocation): - let updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) + let updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) return .IntermediateMessageEntry(updatedMessage, location, monthLocation) case let .MessageEntry(value, reloadAssociatedMessages, reloadPeers): let message = value.message - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 58700fd3b3..76d43c1680 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -1442,7 +1442,7 @@ final class HistoryViewLoadedState { messageMedia.append(media) } } - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: message.associatedStories) return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } case .IntermediateMessageEntry: @@ -1499,7 +1499,7 @@ final class HistoryViewLoadedState { } } } - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: associatedStories) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo, associatedStories: associatedStories) return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } case .IntermediateMessageEntry: diff --git a/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift b/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift new file mode 100644 index 0000000000..e7be5c75fa --- /dev/null +++ b/submodules/Postbox/Sources/MutableMessageHistoryCustomTagSummariesView.swift @@ -0,0 +1,59 @@ +import Foundation + +final class MutableMessageHistoryCustomTagSummariesView: MutablePostboxView { + private let peerId: PeerId + private let namespace: MessageId.Namespace + + fileprivate var tags: [MemoryBuffer: Int] = [:] + + init(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace) { + self.peerId = peerId + self.namespace = namespace + + self.reload(postbox: postbox) + } + + private func reload(postbox: PostboxImpl) { + self.tags.removeAll() + + for tag in postbox.messageHistoryTagsSummaryTable.getCustomTags(tag: [], peerId: self.peerId, threadId: nil, namespace: self.namespace) { + if let summary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: [], peerId: self.peerId, threadId: nil, namespace: self.namespace, customTag: tag)) { + if summary.count > 0 { + self.tags[tag] = Int(summary.count) + } + } + } + } + + func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { + var hasChanges = false + + for key in transaction.currentUpdatedMessageTagSummaries.keys { + if key.peerId == self.peerId && key.namespace == self.namespace && key.customTag != nil { + hasChanges = true + break + } + } + if hasChanges { + self.reload(postbox: postbox) + } + + return hasChanges + } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } + + func immutableView() -> PostboxView { + return MessageHistoryCustomTagSummariesView(self) + } +} + +public final class MessageHistoryCustomTagSummariesView: PostboxView { + public let tags: [MemoryBuffer: Int] + + init(_ view: MutableMessageHistoryCustomTagSummariesView) { + self.tags = view.tags + } +} diff --git a/submodules/Postbox/Sources/PeerThreadCombinedStateTable.swift b/submodules/Postbox/Sources/PeerThreadCombinedStateTable.swift index 2109cf602d..a72499de27 100644 --- a/submodules/Postbox/Sources/PeerThreadCombinedStateTable.swift +++ b/submodules/Postbox/Sources/PeerThreadCombinedStateTable.swift @@ -241,7 +241,7 @@ class PeerThreadsSummaryTable: Table { } for tag in self.seedConfiguration.messageTagsWithThreadSummary { - if let value = tagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: item.threadId, namespace: 0)) { + if let value = tagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: item.threadId, namespace: 0, customTag: nil)) { tagSummaries.append(StoredPeerThreadsSummary.ThreadsTagSummary(tag: tag, count: value.count)) } } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 9931b697a7..77eacab984 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1035,14 +1035,19 @@ public final class Transaction { return self.postbox?.getPendingMessageAction(type: type, id: id) } - public func getMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> MessageHistoryTagNamespaceSummary? { + public func getMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, customTag: MemoryBuffer?) -> MessageHistoryTagNamespaceSummary? { assert(!self.disposed) - return self.postbox?.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace)) + return self.postbox?.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace, customTag: customTag)) } - public func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) { + public func getMessageTagSummaryCustomTags(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> [MemoryBuffer] { assert(!self.disposed) - self.postbox?.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace, count: count, maxId: maxId) + return self.postbox!.messageHistoryTagsSummaryTable.getCustomTags(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace) + } + + public func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, customTag: MemoryBuffer?, count: Int32, maxId: MessageId.Id) { + assert(!self.disposed) + self.postbox?.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace, customTag: customTag, count: count, maxId: maxId) } public func getPendingMessageActionsSummary(peerId: PeerId, type: PendingMessageActionType, namespace: MessageId.Namespace) -> Int32? { @@ -1109,9 +1114,9 @@ public final class Transaction { return view! } - public func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) { + public func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags, customTag: MemoryBuffer?) { assert(!self.disposed) - self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, threadId: threadId, namespace: namespace, tagMask: tagMask) + self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, threadId: threadId, namespace: namespace, tagMask: tagMask, customTag: customTag) } public func removeInvalidatedMessageHistoryTagsSummaryEntry(_ entry: InvalidatedMessageHistoryTagsSummaryEntry) { @@ -1119,6 +1124,10 @@ public final class Transaction { self.postbox?.removeInvalidatedMessageHistoryTagsSummaryEntry(entry) } + public func removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) { + self.postbox?.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: threadId, namespace: namespace, tagMask: tagMask) + } + public func getRelativeUnreadChatListIndex(filtered: Bool, position: ChatListRelativePosition, groupId: PeerGroupId) -> ChatListIndex? { assert(!self.disposed) return self.postbox?.getRelativeUnreadChatListIndex(currentTransaction: self, filtered: filtered, position: position, groupId: groupId) @@ -2857,8 +2866,8 @@ final class PostboxImpl { return self.pendingMessageActionsTable.getAction(id: id, type: type) } - fileprivate func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) { - let key = MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace) + fileprivate func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, customTag: MemoryBuffer?, count: Int32, maxId: MessageId.Id) { + let key = MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace, customTag: customTag) self.messageHistoryTagsSummaryTable.replace(key: key, count: count, maxId: maxId, updatedSummaries: &self.currentUpdatedMessageTagSummaries) } @@ -4025,14 +4034,18 @@ final class PostboxImpl { } } - fileprivate func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) { - self.invalidatedMessageHistoryTagsSummaryTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId), operations: &self.currentInvalidateMessageTagSummaries) + fileprivate func invalidateMessageHistoryTagsSummary(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags, customTag: MemoryBuffer?) { + self.invalidatedMessageHistoryTagsSummaryTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask, threadId: threadId, customTag: customTag), operations: &self.currentInvalidateMessageTagSummaries) } fileprivate func removeInvalidatedMessageHistoryTagsSummaryEntry(_ entry: InvalidatedMessageHistoryTagsSummaryEntry) { self.invalidatedMessageHistoryTagsSummaryTable.remove(entry, operations: &self.currentInvalidateMessageTagSummaries) } + fileprivate func removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tagMask: MessageTags) { + self.invalidatedMessageHistoryTagsSummaryTable.removeEntriesWithCustomTags(peerId: peerId, threadId: threadId, namespace: namespace, tagMask: tagMask, operations: &self.currentInvalidateMessageTagSummaries) + } + fileprivate func getRelativeUnreadChatListIndex(currentTransaction: Transaction, filtered: Bool, position: ChatListRelativePosition, groupId: PeerGroupId) -> ChatListIndex? { return self.chatListTable.getRelativeUnreadChatListIndex(postbox: self, currentTransaction: currentTransaction, filtered: filtered, position: position, groupId: groupId) } diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index 1aff6733b7..f442e12b9a 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -49,7 +49,7 @@ func resolveChatListMessageTagSummaryResultCalculation(postbox: PostboxImpl, pee guard let calculation = calculation else { return nil } - let addSummary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: calculation.addCount.tag, peerId: peerId, threadId: threadId, namespace: calculation.addCount.namespace)) + let addSummary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: calculation.addCount.tag, peerId: peerId, threadId: threadId, namespace: calculation.addCount.namespace, customTag: nil)) let subtractSummary = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(peerId, calculation.subtractCount.namespace, calculation.subtractCount.type)) let count = (addSummary?.count ?? 0) - subtractSummary return count > 0 diff --git a/submodules/Postbox/Sources/ValueBoxKey.swift b/submodules/Postbox/Sources/ValueBoxKey.swift index f192bde0f6..021adfaa69 100644 --- a/submodules/Postbox/Sources/ValueBoxKey.swift +++ b/submodules/Postbox/Sources/ValueBoxKey.swift @@ -47,6 +47,11 @@ public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Compara } } + public func setBytes(_ offset: Int, value: UnsafeRawBufferPointer) { + assert(offset >= 0 && offset + value.count <= self.length) + memcpy(self.memory + offset, value.baseAddress!.assumingMemoryBound(to: UInt8.self), value.count) + } + public func setInt32(_ offset: Int, value: Int32) { assert(offset >= 0 && offset + 4 <= self.length) var bigEndianValue = Int32(bigEndian: value) @@ -92,6 +97,13 @@ public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Compara return value } + public func getMemoryBuffer(_ offset: Int, length: Int) -> MemoryBuffer { + assert(offset >= 0 && offset + length <= self.length) + let result = MemoryBuffer(memory: malloc(length)!, capacity: length, length: length, freeWhenDone: true) + memcpy(result.memory, self.memory + offset, length) + return result + } + public func getInt32(_ offset: Int) -> Int32 { assert(offset >= 0 && offset + 4 <= self.length) var value: Int32 = 0 @@ -259,7 +271,7 @@ public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Compara } } -private func mdb_cmp_memn(_ a_memory: UnsafeMutableRawPointer, _ a_length: Int, _ b_memory: UnsafeMutableRawPointer, _ b_length: Int) -> Int +func mdb_cmp_memn(_ a_memory: UnsafeMutableRawPointer, _ a_length: Int, _ b_memory: UnsafeMutableRawPointer, _ b_length: Int) -> Int { var diff: Int = 0 var len_diff: Int = 0 diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 67ccf5d34c..ffbd9a35ac 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -12,7 +12,8 @@ public enum PostboxViewKey: Hashable { case pendingMessageActions(type: PendingMessageActionType) case invalidatedMessageHistoryTagSummaries(peerId: PeerId?, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace) - case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) + case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, customTag: MemoryBuffer?) + case historyCustomTagSummariesView(peerId: PeerId, namespace: MessageId.Namespace) case cachedPeerData(peerId: PeerId) case unreadCounts(items: [UnreadMessageCountsItem]) case combinedReadState(peerId: PeerId, handleThreads: Bool) @@ -78,11 +79,15 @@ public enum PostboxViewKey: Hashable { hasher.combine(type) hasher.combine(peerId) hasher.combine(namespace) - case let .historyTagSummaryView(tag, peerId, threadId, namespace): + case let .historyTagSummaryView(tag, peerId, threadId, namespace, customTag): hasher.combine(tag) hasher.combine(peerId) hasher.combine(threadId) hasher.combine(namespace) + hasher.combine(customTag) + case let .historyCustomTagSummariesView(peerId, namespace): + hasher.combine(peerId) + hasher.combine(namespace) case let .cachedPeerData(peerId): hasher.combine(peerId) case .unreadCounts: @@ -230,8 +235,14 @@ public enum PostboxViewKey: Hashable { } else { return false } - case let .historyTagSummaryView(tag, peerId, threadId, namespace): - if case .historyTagSummaryView(tag, peerId, threadId, namespace) = rhs { + case let .historyTagSummaryView(tag, peerId, threadId, namespace, customTag): + if case .historyTagSummaryView(tag, peerId, threadId, namespace, customTag) = rhs { + return true + } else { + return false + } + case let .historyCustomTagSummariesView(peerId, namespace): + if case .historyCustomTagSummariesView(peerId, namespace) = rhs { return true } else { return false @@ -474,8 +485,10 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace) case let .pendingMessageActionsSummary(type, peerId, namespace): return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace) - case let .historyTagSummaryView(tag, peerId, threadId, namespace): - return MutableMessageHistoryTagSummaryView(postbox: postbox, tag: tag, peerId: peerId, threadId: threadId, namespace: namespace) + case let .historyTagSummaryView(tag, peerId, threadId, namespace, customTag): + return MutableMessageHistoryTagSummaryView(postbox: postbox, tag: tag, peerId: peerId, threadId: threadId, namespace: namespace, customTag: customTag) + case let .historyCustomTagSummariesView(peerId, namespace): + return MutableMessageHistoryCustomTagSummariesView(postbox: postbox, peerId: peerId, namespace: namespace) case let .cachedPeerData(peerId): return MutableCachedPeerDataView(postbox: postbox, peerId: peerId) case let .unreadCounts(items): diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 0a2304340b..e687516ee1 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -328,6 +328,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { private var hasPremium: Bool? private var hasPremiumDisposable: Disposable? + private var allPresetReactionsAreAvailable: Bool + private var genericReactionEffectDisposable: Disposable? private var genericReactionEffect: String? @@ -371,7 +373,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, title: String? = nil, alwaysAllowPremiumReactions: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) { + public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, items: [ReactionContextItem], selectedItems: Set, title: String? = nil, alwaysAllowPremiumReactions: Bool, allPresetReactionsAreAvailable: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) { self.context = context self.presentationData = presentationData self.items = items @@ -472,6 +474,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions + self.allPresetReactionsAreAvailable = allPresetReactionsAreAvailable super.init() @@ -1422,7 +1425,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { isCustom: false ) - if case .custom = reactionItem.updateMessageReaction, let hasPremium = strongSelf.hasPremium, !hasPremium { + if case .locked = item.icon { strongSelf.premiumReactionsSelected?(reactionItem.stillAnimation) } else { strongSelf.customReactionSource = (sourceView, sourceRect, sourceLayer, reactionItem) @@ -1444,7 +1447,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { isCustom: true ) strongSelf.customReactionSource = (sourceView, sourceRect, sourceLayer, reactionItem) - if case .custom = reactionItem.updateMessageReaction, let hasPremium = strongSelf.hasPremium, !hasPremium { + if case .locked = item.icon { strongSelf.premiumReactionsSelected?(reactionItem.stillAnimation) } else { strongSelf.reactionSelected?(reactionItem.updateMessageReaction, isLongPress) @@ -2423,7 +2426,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } else if let reaction = self.reaction(at: point) { switch reaction { case let .reaction(reactionItem): - if case .custom = reactionItem.updateMessageReaction, let hasPremium = self.hasPremium, !hasPremium { + if case .custom = reactionItem.updateMessageReaction, let hasPremium = self.hasPremium, !hasPremium, !self.allPresetReactionsAreAvailable { self.premiumReactionsSelected?(reactionItem.stillAnimation) } else { self.reactionSelected?(reactionItem.updateMessageReaction, false) diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 0285e9e6dc..3b9ff80950 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -50,12 +50,13 @@ public struct SearchBarToken { public let iconOffset: CGFloat? public let peer: (EnginePeer, AccountContext, PresentationTheme)? public let isTag: Bool + public let reaction: MessageReaction.Reaction? public let emojiFile: TelegramMediaFile? public let title: String public let style: Style? public let permanent: Bool - public init(id: AnyHashable, context: AccountContext? = nil, icon: UIImage?, iconOffset: CGFloat? = 0.0, peer: (EnginePeer, AccountContext, PresentationTheme)? = nil, isTag: Bool = false, emojiFile: TelegramMediaFile? = nil, title: String, style: Style? = nil, permanent: Bool) { + public init(id: AnyHashable, context: AccountContext? = nil, icon: UIImage?, iconOffset: CGFloat? = 0.0, peer: (EnginePeer, AccountContext, PresentationTheme)? = nil, isTag: Bool = false, reaction: MessageReaction.Reaction? = nil, emojiFile: TelegramMediaFile? = nil, title: String, style: Style? = nil, permanent: Bool) { self.id = id self.context = context self.icon = icon @@ -63,6 +64,7 @@ public struct SearchBarToken { self.peer = peer self.isTag = isTag self.emojiFile = emojiFile + self.reaction = reaction self.title = title self.style = style self.permanent = permanent @@ -230,7 +232,12 @@ private final class TokenNode: ASDisplayNode { emojiView = ComponentView() self.emojiView = emojiView } - let emojiViewSize = emojiView.update( + let emojiSize = CGSize(width: 14.0, height: 14.0) + var visibleEmojiSize = emojiSize + if case .builtin = self.token.reaction { + visibleEmojiSize = CGSize(width: visibleEmojiSize.width * 2.0, height: visibleEmojiSize.height * 2.0) + } + let _ = emojiView.update( transition: .immediate, component: AnyComponent(EmojiStatusComponent( context: context, @@ -238,7 +245,7 @@ private final class TokenNode: ASDisplayNode { animationRenderer: context.animationRenderer, content: .animation( content: .file(file: emojiFile), - size: CGSize(width: 32.0, height: 32.0), + size: visibleEmojiSize, placeholderColor: self.theme.primaryText.withMultipliedAlpha(0.2), themeColor: self.theme.primaryText, loopMode: .forever @@ -249,22 +256,20 @@ private final class TokenNode: ASDisplayNode { emojiFileUpdated: nil )), environment: {}, - containerSize: CGSize(width: 14.0, height: 14.0) + containerSize: visibleEmojiSize ) if let emojiComponentView = emojiView.view { if emojiComponentView.superview == nil { self.containerNode.view.addSubview(emojiComponentView) } - emojiComponentView.frame = CGRect(origin: CGPoint(x: leftInset + 2.0, y: floor((height - emojiViewSize.height) * 0.5)), size: emojiViewSize) + let emojiFrame = CGRect(origin: CGPoint(x: leftInset + 2.0, y: floor((height - emojiSize.height) * 0.5)), size: emojiSize) + emojiComponentView.frame = visibleEmojiSize.centered(around: emojiFrame.center) } - emojiFileSize = emojiViewSize + emojiFileSize = emojiSize } if self.token.isTag { leftInset += 2.0 } - if let emojiFileSize { - leftInset += emojiFileSize.width + 7.0 - } let iconSize = self.token.icon?.size ?? CGSize() let titleSize = self.titleNode.measure(CGSize(width: constrainedSize.width - 6.0, height: constrainedSize.height)) @@ -272,8 +277,12 @@ private final class TokenNode: ASDisplayNode { if !iconSize.width.isZero { width += iconSize.width + 7.0 } + if let emojiFileSize { + leftInset += emojiFileSize.width + 6.0 + width += emojiFileSize.width + 6.0 + } if self.token.isTag { - width += 19.0 + width += 16.0 } let size: CGSize diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 75b5b852b3..948bb21b79 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -167,22 +167,22 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) - let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) let width: CGFloat diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index c4190b3f5a..ba5ee38b67 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -149,7 +149,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: []) - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true) var node: ListViewItemNode? if let current = currentNode { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index ad1076b633..47b3630090 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -35,12 +35,13 @@ private final class PrivacyAndSecurityControllerArguments { let openTwoStepVerification: (TwoStepVerificationAccessConfiguration?) -> Void let openActiveSessions: () -> Void let toggleArchiveAndMuteNonContacts: (Bool) -> Void + let toggleOnlyAllowPremiumNonContacts: (Bool) -> Void let setupAccountAutoremove: () -> Void let setupMessageAutoremove: () -> Void let openDataSettings: () -> Void let openEmailSettings: (String?) -> Void - init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openBioPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, setupMessageAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void) { + init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openBioPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, toggleOnlyAllowPremiumNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, setupMessageAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void) { self.account = account self.openBlockedUsers = openBlockedUsers self.openLastSeenPrivacy = openLastSeenPrivacy @@ -55,6 +56,7 @@ private final class PrivacyAndSecurityControllerArguments { self.openTwoStepVerification = openTwoStepVerification self.openActiveSessions = openActiveSessions self.toggleArchiveAndMuteNonContacts = toggleArchiveAndMuteNonContacts + self.toggleOnlyAllowPremiumNonContacts = toggleOnlyAllowPremiumNonContacts self.setupAccountAutoremove = setupAccountAutoremove self.setupMessageAutoremove = setupMessageAutoremove self.openDataSettings = openDataSettings @@ -66,6 +68,7 @@ private enum PrivacyAndSecuritySection: Int32 { case general case privacy case autoArchive + case onlyAllowPremiumNonContacts case account case messageAutoremove case dataSettings @@ -105,6 +108,8 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { case autoArchiveHeader(String) case autoArchive(String, Bool) case autoArchiveInfo(String) + case onlyAllowPremiumNonContacts(String, Bool) + case onlyAllowPremiumNonContactsInfo(String) case accountHeader(PresentationTheme, String) case accountTimeout(PresentationTheme, String, String) case accountInfo(PresentationTheme, String) @@ -121,6 +126,8 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { return PrivacyAndSecuritySection.privacy.rawValue case .autoArchiveHeader, .autoArchive, .autoArchiveInfo: return PrivacyAndSecuritySection.autoArchive.rawValue + case .onlyAllowPremiumNonContacts, .onlyAllowPremiumNonContactsInfo: + return PrivacyAndSecuritySection.onlyAllowPremiumNonContacts.rawValue case .accountHeader, .accountTimeout, .accountInfo: return PrivacyAndSecuritySection.account.rawValue case .dataSettings, .dataSettingsInfo: @@ -172,16 +179,20 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { return 20 case .autoArchiveInfo: return 21 - case .accountHeader: + case .onlyAllowPremiumNonContacts: return 22 - case .accountTimeout: + case .onlyAllowPremiumNonContactsInfo: return 23 - case .accountInfo: + case .accountHeader: return 24 - case .dataSettings: + case .accountTimeout: return 25 - case .dataSettingsInfo: + case .accountInfo: return 26 + case .dataSettings: + return 27 + case .dataSettingsInfo: + return 28 } } @@ -301,6 +312,18 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { } else { return false } + case let .onlyAllowPremiumNonContacts(text, value): + if case .onlyAllowPremiumNonContacts(text, value) = rhs { + return true + } else { + return false + } + case let .onlyAllowPremiumNonContactsInfo(text): + if case .onlyAllowPremiumNonContactsInfo(text) = rhs { + return true + } else { + return false + } case let .accountHeader(lhsTheme, lhsText): if case let .accountHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -425,6 +448,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { }, tag: PrivacyAndSecurityEntryTag.autoArchive) case let .autoArchiveInfo(text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .onlyAllowPremiumNonContacts(text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleOnlyAllowPremiumNonContacts(value) + }, tag: PrivacyAndSecurityEntryTag.autoArchive) + case let .onlyAllowPremiumNonContactsInfo(text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .accountHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .accountTimeout(_, text, value): @@ -447,6 +476,7 @@ private struct PrivacyAndSecurityControllerState: Equatable { var updatingAccountTimeoutValue: Int32? = nil var updatingAutomaticallyArchiveAndMuteNonContacts: Bool? = nil var updatingMessageAutoremoveTimeoutValue: Int32? = nil + var updatingOnlyAllowPremiumNonContacts: Bool? = nil } private func countForSelectivePeers(_ peers: [PeerId: SelectivePrivacyPeer]) -> Int { @@ -611,6 +641,15 @@ private func privacyAndSecurityControllerEntries( entries.append(.autoArchiveInfo(presentationData.strings.PrivacySettings_AutoArchiveInfo)) } + //TODO:localize + let onlyAllowPremiumNonContactsValue = state.updatingOnlyAllowPremiumNonContacts ?? privacySettings?.globalSettings.nonContactChatsRequirePremium ?? false + entries.append(.onlyAllowPremiumNonContacts("Only Allow Premium Chats", onlyAllowPremiumNonContactsValue)) + if !onlyAllowPremiumNonContactsValue { + entries.append(.onlyAllowPremiumNonContactsInfo("Non-premium users are allowed to send you a message.")) + } else { + entries.append(.onlyAllowPremiumNonContactsInfo("Non-premium users are not allowed to send you a message.")) + } + entries.append(.accountHeader(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountTitle.uppercased())) if let privacySettings = privacySettings { let value: Int32 @@ -775,7 +814,7 @@ public func privacyAndSecurityController( |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, bio: value.bio, globalSettings: value.globalSettings, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, bio: value.bio, globalSettings: updatedGlobalSettings ?? value.globalSettings, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout))) } return .complete() } @@ -1061,6 +1100,36 @@ public func privacyAndSecurityController( return state } })) + }, toggleOnlyAllowPremiumNonContacts: { settingValue in + updateState { state in + var state = state + state.updatingOnlyAllowPremiumNonContacts = settingValue + return state + } + let applyTimeout: Signal = privacySettingsPromise.get() + |> filter { $0 != nil } + |> take(1) + |> deliverOnMainQueue + |> mapToSignal { value -> Signal in + if let value = value { + var globalSettings = value.globalSettings + globalSettings.nonContactChatsRequirePremium = settingValue + + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, bio: value.bio, globalSettings: globalSettings, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout))) + } + return .complete() + } + + updateAutoArchiveDisposable.set((context.engine.privacy.updateNonContactChatsRequirePremium(value: settingValue) + |> mapToSignal { _ -> Signal in } + |> then(applyTimeout) + |> deliverOnMainQueue).start(completed: { + updateState { state in + var state = state + state.updatingOnlyAllowPremiumNonContacts = nil + return state + } + })) }, setupAccountAutoremove: { let signal = privacySettingsPromise.get() |> take(1) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index f5f440f7a3..3fc31c3295 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -907,7 +907,7 @@ func selectivePrivacySettingsController( } //TODO:replace hideReadTimeEnabled with actual value - let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, enableForCloseFriends: initialEnableForCloseFriends, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callP2PEnableForCloseFriends: initialCallEnableForCloseFriends, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration, phoneDiscoveryEnabled: phoneDiscoveryEnabled, hideReadTimeEnabled: globalSettings?.keepArchivedFolders, uploadedPhoto: nil) + let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, enableForCloseFriends: initialEnableForCloseFriends, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callP2PEnableForCloseFriends: initialCallEnableForCloseFriends, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration, phoneDiscoveryEnabled: phoneDiscoveryEnabled, hideReadTimeEnabled: globalSettings?.hideReadTime, uploadedPhoto: nil) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -1314,10 +1314,8 @@ func selectivePrivacySettingsController( var updateGlobalSettingsSignal: Signal = Signal.complete() var updatedGlobalSettings: GlobalPrivacySettings? - if let updateHideReadTime = arguments.updateHideReadTime, let globalSettings { - //TODO:update global settings - let _ = updateHideReadTime - updatedGlobalSettings = GlobalPrivacySettings(automaticallyArchiveAndMuteNonContacts: globalSettings.automaticallyArchiveAndMuteNonContacts, keepArchivedUnmuted: globalSettings.keepArchivedUnmuted, keepArchivedFolders: globalSettings.keepArchivedFolders) + if let _ = arguments.updateHideReadTime, let globalSettings { + updatedGlobalSettings = GlobalPrivacySettings(automaticallyArchiveAndMuteNonContacts: globalSettings.automaticallyArchiveAndMuteNonContacts, keepArchivedUnmuted: globalSettings.keepArchivedUnmuted, keepArchivedFolders: globalSettings.keepArchivedFolders, hideReadTime: hideReadTimeEnabled ?? globalSettings.hideReadTime, nonContactChatsRequirePremium: globalSettings.nonContactChatsRequirePremium) if let updatedGlobalSettings { updateGlobalSettingsSignal = context.engine.privacy.updateGlobalPrivacySettings(settings: updatedGlobalSettings) } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 1e9f8351ef..5968674bb2 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -262,6 +262,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: author, text: text, @@ -432,22 +433,22 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) - let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) let width: CGFloat diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index c258319120..5fa37113bb 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -409,6 +409,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: author, text: text, @@ -588,20 +589,20 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { var sampleMessages: [Message] = [] - let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message1) - let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message2) - let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message3) - let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message4.id] = message4 sampleMessages.append(message4) - let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message5.id] = message5 sampleMessages.append(message5) @@ -609,13 +610,13 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message7) - let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message8) items = sampleMessages.reversed().map { message in diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 8e4c892102..b26e8f3cc4 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -164,10 +164,10 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) if let (author, text) = messageItem.reply { peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: messageItem.nameColor, backgroundEmojiId: messageItem.backgroundEmojiId, profileColor: nil, profileBackgroundEmojiId: nil) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) } diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 97993eb59b..c0040a2c68 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -972,8 +972,8 @@ func applyLoadMessageHistoryThreadsResults(accountPeerId: PeerId, transaction: T transaction.setMessageHistoryThreadInfo(peerId: result.peerId, threadId: item.threadId, info: info) - transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: item.unreadMentionsCount, maxId: item.topMessage) - transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: item.unreadReactionsCount, maxId: item.topMessage) + transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: item.unreadMentionsCount, maxId: item.topMessage) + transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: item.unreadReactionsCount, maxId: item.topMessage) if item.topMessage != 0 { transaction.removeHole(peerId: result.peerId, threadId: item.threadId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: item.topMessage ... (Int32.max - 1)) diff --git a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift index 55b02b323f..bd375d0806 100644 --- a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift +++ b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift @@ -251,21 +251,29 @@ public struct GlobalPrivacySettings: Equatable, Codable { public static var `default` = GlobalPrivacySettings( automaticallyArchiveAndMuteNonContacts: false, keepArchivedUnmuted: true, - keepArchivedFolders: true + keepArchivedFolders: true, + hideReadTime: false, + nonContactChatsRequirePremium: false ) public var automaticallyArchiveAndMuteNonContacts: Bool public var keepArchivedUnmuted: Bool public var keepArchivedFolders: Bool + public var hideReadTime: Bool + public var nonContactChatsRequirePremium: Bool public init( automaticallyArchiveAndMuteNonContacts: Bool, keepArchivedUnmuted: Bool, - keepArchivedFolders: Bool + keepArchivedFolders: Bool, + hideReadTime: Bool, + nonContactChatsRequirePremium: Bool ) { self.automaticallyArchiveAndMuteNonContacts = automaticallyArchiveAndMuteNonContacts self.keepArchivedUnmuted = keepArchivedUnmuted self.keepArchivedFolders = keepArchivedFolders + self.hideReadTime = hideReadTime + self.nonContactChatsRequirePremium = nonContactChatsRequirePremium } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index b290d3f72d..5dabfda0b3 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2046,8 +2046,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, network: Netwo transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry) } - transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) - transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: unreadMentionsCount, maxId: topMessage) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: unreadReactionsCount, maxId: topMessage) case .forumTopicDeleted: break } @@ -4028,7 +4028,7 @@ func replayFinalState( case let .UpdatePeerChatUnreadMark(peerId, namespace, value): transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: value, interactive: false) case let .ResetMessageTagSummary(peerId, tag, namespace, count, range): - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: tag, namespace: namespace, count: count, maxId: range.maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: tag, namespace: namespace, customTag: nil, count: count, maxId: range.maxId) if count == 0 { transaction.removeHole(peerId: peerId, threadId: nil, namespace: namespace, space: .tag(tag), range: 1 ... (Int32.max - 1)) if tag == .unseenPersonalMessage { @@ -4608,8 +4608,8 @@ func replayFinalState( } else { assertionFailure() } - transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId) - transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadMentionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadReactionCount, maxId: data.topMessageId) } case let .UpdateStory(peerId, story): var updatedPeerEntries: [StoryItemsTableEntry] = transaction.getStoryItems(peerId: peerId) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 97a5986320..61bf2a6e8e 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1392,13 +1392,13 @@ public final class AccountViewTracker { }) } - if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), summary.count > 0 { + if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil), summary.count > 0 { var maxId: Int32 = summary.range.maxId if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { maxId = index.id.id } - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: 0, maxId: maxId) addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) } @@ -1475,13 +1475,13 @@ public final class AccountViewTracker { }) } - if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud) { + if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil) { var maxId: Int32 = summary.range.maxId if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { maxId = index.id.id } - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: 0, maxId: maxId) addSynchronizeMarkAllUnseenReactionsOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) } @@ -2202,10 +2202,10 @@ public final class AccountViewTracker { public func unseenPersonalMessagesAndReactionCount(peerId: PeerId, threadId: Int64?) -> Signal<(mentionCount: Int32, reactionCount: Int32), NoError> { if let account = self.account { let pendingMentionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud) - let summaryMentionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud) + let summaryMentionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, customTag: nil) let pendingReactionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .readReaction, peerId: peerId, namespace: Namespaces.Message.Cloud) - let summaryReactionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenReaction, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud) + let summaryReactionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenReaction, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, customTag: nil) return account.postbox.combinedView(keys: [pendingMentionsKey, summaryMentionsKey, pendingReactionsKey, summaryReactionsKey]) |> map { views -> (mentionCount: Int32, reactionCount: Int32) in diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index e78bfe6d54..c8baac38e2 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -955,8 +955,8 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId if let entry = StoredMessageHistoryThreadInfo(data.data) { transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) } - transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId) - transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadMentionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadReactionCount, maxId: data.topMessageId) } transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) @@ -1024,10 +1024,10 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId } for (peerId, summary) in fetchedChats.mentionTagSummaries { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (peerId, summary) in fetchedChats.reactionTagSummaries { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (groupId, summary) in fetchedChats.folderSummaries { diff --git a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift index 7acb49ea2a..9289f64799 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift @@ -377,7 +377,7 @@ private func synchronizeUnseenPersonalMentionsTag(postbox: Postbox, network: Net } return postbox.transaction { transaction -> Void in - transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadMentionsCount, maxId: apiTopMessage) + transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, customTag: nil, count: apiUnreadMentionsCount, maxId: apiTopMessage) } } else { return .complete() @@ -419,7 +419,7 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e } return postbox.transaction { transaction -> Void in - transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadReactionsCount, maxId: apiTopMessage) + transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, customTag: nil, count: apiUnreadReactionsCount, maxId: apiTopMessage) } } else { return .complete() @@ -435,7 +435,7 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e } |> switchToLatest } -func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, threadId: Int64) -> Signal { +func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, threadId: Int64?) -> Signal { return Signal { _ in let helper = Atomic(value: ManagedConsumePersonalMessagesActionsHelper()) @@ -445,6 +445,10 @@ func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Net if let v = view.views[invalidateKey] as? InvalidatedMessageHistoryTagSummariesView { invalidateEntries = v.entries } + if invalidateEntries.contains(where: { $0.key.customTag != nil }) { + invalidateEntries = invalidateEntries.filter({ $0.key.customTag == nil }) + invalidateEntries.insert(InvalidatedMessageHistoryTagsSummaryEntry(key: InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: Namespaces.Message.Cloud, tagMask: [], threadId: threadId, customTag: MemoryBuffer()), version: 0)) + } let (disposeOperations, _, beginValidateOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)], beginValidateOperations: [(InvalidatedMessageHistoryTagsSummaryEntry, MetaDisposable)]) in return helper.update(entries: [], invalidateEntries: invalidateEntries) @@ -455,11 +459,29 @@ func managedSynchronizeMessageHistoryTagSummaries(postbox: Postbox, network: Net } for (entry, disposable) in beginValidateOperations { - let signal = synchronizeMessageHistoryTagSummary(postbox: postbox, network: network, entry: entry) - |> then(postbox.transaction { transaction -> Void in - transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry) - }) - disposable.set(signal.start()) + if entry.key.customTag != nil { + if peerId == stateManager.accountPeerId { + let signal = synchronizeSavedMessageTags(postbox: postbox, network: network, peerId: peerId) + |> map { _ -> Void in + } + |> then(postbox.transaction { transaction -> Void in + transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: []) + }) + disposable.set(signal.start()) + } else { + assertionFailure() + let signal = postbox.transaction { transaction -> Void in + transaction.removeInvalidatedMessageHistoryTagsSummaryEntriesWithCustomTags(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: []) + } + disposable.set(signal.start()) + } + } else { + let signal = synchronizeMessageHistoryTagSummary(postbox: postbox, network: network, entry: entry) + |> then(postbox.transaction { transaction -> Void in + transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry) + }) + disposable.set(signal.start()) + } } }) @@ -494,7 +516,7 @@ private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Netw switch result { case let .channelMessages(_, _, count, _, messages, _, _, _): let topId: Int32 = messages.first?.id(namespace: Namespaces.Message.Cloud)?.id ?? 1 - transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: threadId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: count, maxId: topId) + transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: threadId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, customTag: nil, count: count, maxId: topId) default: break } @@ -506,3 +528,62 @@ private func synchronizeMessageHistoryTagSummary(postbox: Postbox, network: Netw } |> switchToLatest } + +private func synchronizeSavedMessageTags(postbox: Postbox, network: Network, peerId: PeerId) -> Signal { + return (network.request(Api.functions.messages.getSavedReactionTags(hash: 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .complete() + } + + switch result { + case .savedReactionTagsNotModified: + return .complete() + case let .savedReactionTags(tags, _): + var customFileIds: [Int64] = [] + var parsedTags: [SavedMessageTags.Tag] = [] + for tag in tags { + switch tag { + case let .savedReactionTag(_, reaction, title, count): + guard let reaction = MessageReaction.Reaction(apiReaction: reaction) else { + continue + } + parsedTags.append(SavedMessageTags.Tag( + reaction: reaction, + title: title, + count: Int(count) + )) + + if case let .custom(fileId) = reaction { + customFileIds.append(fileId) + } + } + } + + let _ = customFileIds + + return postbox.transaction { transaction -> Void in + let previousTags = transaction.getMessageTagSummaryCustomTags(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud) + + let topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud)?.id ?? 1 + + var validTags: [MemoryBuffer] = [] + for tag in parsedTags { + let customTag = ReactionsMessageAttribute.messageTag(reaction: tag.reaction) + validTags.append(customTag) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: customTag, count: Int32(tag.count), maxId: topMessageId) + } + for tag in previousTags { + if !validTags.contains(tag) { + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: tag, count: 0, maxId: topMessageId) + } + } + } + |> ignoreValues + } + }) +} diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index bcaf3dbe7e..20b9c662dd 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -1895,6 +1895,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: nil, text: contents.text, diff --git a/submodules/TelegramCore/Sources/State/ResetState.swift b/submodules/TelegramCore/Sources/State/ResetState.swift index fe9ddcf50f..c56a33721a 100644 --- a/submodules/TelegramCore/Sources/State/ResetState.swift +++ b/submodules/TelegramCore/Sources/State/ResetState.swift @@ -46,8 +46,8 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer if let entry = StoredMessageHistoryThreadInfo(data.data) { transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) } - transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId) - transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadMentionCount, maxId: data.topMessageId) + transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadReactionCount, maxId: data.topMessageId) } transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) @@ -125,16 +125,24 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer } for (peerId, summary) in fetchedChats.mentionTagSummaries { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (peerId, summary) in fetchedChats.reactionTagSummaries { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (groupId, summary) in fetchedChats.folderSummaries { transaction.resetPeerGroupSummary(groupId: groupId, namespace: Namespaces.Message.Cloud, summary: summary) } + let savedMessageTags = transaction.getMessageTagSummaryCustomTags(peerId: accountPeerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud) + if !savedMessageTags.isEmpty { + for tag in savedMessageTags { + transaction.replaceMessageTagSummary(peerId: accountPeerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: tag, count: 0, maxId: 1) + } + transaction.invalidateMessageHistoryTagsSummary(peerId: accountPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: [], customTag: savedMessageTags[0]) + } + transaction.reindexUnreadCounters() if let currentState = transaction.getState() as? AuthorizedAccountState { diff --git a/submodules/TelegramCore/Sources/State/SavedMessageTags.swift b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift index 44207a6a23..c6172a7552 100644 --- a/submodules/TelegramCore/Sources/State/SavedMessageTags.swift +++ b/submodules/TelegramCore/Sources/State/SavedMessageTags.swift @@ -104,6 +104,13 @@ func _internal_savedMessageTags(postbox: Postbox) -> Signal ItemCacheEntryId { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) + + return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.savedMessageTags, key: key) +} + func _internal_savedMessageTags(transaction: Transaction) -> SavedMessageTags? { let key = ValueBoxKey(length: 8) key.setInt64(0, value: 0) diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 3ef225aaab..b54867d1ff 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 171 + return 172 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index 437fdcd7d7..418b9acd4b 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -1,8 +1,9 @@ +import Foundation import Postbox import TelegramApi public struct MessageReaction: Equatable, PostboxCoding, Codable { - public enum Reaction: Hashable, Codable, PostboxCoding { + public enum Reaction: Hashable, Comparable, Codable, PostboxCoding { case builtin(String) case custom(Int64) @@ -43,6 +44,25 @@ public struct MessageReaction: Equatable, PostboxCoding, Codable { encoder.encodeInt64(fileId, forKey: "cfid") } } + + public static func <(lhs: Reaction, rhs: Reaction) -> Bool { + switch lhs { + case let .builtin(lhsValue): + switch rhs { + case let .builtin(rhsValue): + return lhsValue < rhsValue + case .custom: + return true + } + case let .custom(lhsValue): + switch rhs { + case .builtin: + return false + case let .custom(rhsValue): + return lhsValue < rhsValue + } + } + } } public var value: Reaction @@ -145,6 +165,71 @@ extension MessageReaction.Reaction { } public final class ReactionsMessageAttribute: Equatable, MessageAttribute { + public static func messageTag(reaction: MessageReaction.Reaction) -> MemoryBuffer { + let buffer = WriteBuffer() + var prefix: UInt8 = 0 + buffer.write(&prefix, offset: 0, length: 1) + switch reaction { + case let .builtin(value): + var stringData = value.data(using: .utf8) ?? Data() + var length: UInt8 = UInt8(clamping: stringData.count) + if stringData.count > Int(length) { + stringData.count = Int(length) + } + var typeId: UInt8 = 0 + buffer.write(&typeId, offset: 0, length: 1) + + buffer.write(&length, offset: 0, length: 1) + buffer.write(stringData) + case var .custom(fileId): + var typeId: UInt8 = 1 + buffer.write(&typeId, offset: 0, length: 1) + buffer.write(&fileId, offset: 0, length: 8) + } + + return buffer + } + + public static func reactionFromMessageTag(tag: MemoryBuffer) -> MessageReaction.Reaction? { + if tag.length < 2 { + return nil + } + + let readBuffer = ReadBuffer(memoryBufferNoCopy: tag) + + var prefix: UInt8 = 0 + readBuffer.read(&prefix, offset: 0, length: 1) + if prefix != 0 { + return nil + } + + var typeId: UInt8 = 0 + readBuffer.read(&typeId, offset: 0, length: 1) + switch typeId { + case 0: + var length8: UInt8 = 0 + readBuffer.read(&length8, offset: 0, length: 1) + let length = Int(length8) + if readBuffer.offset + length > readBuffer.length { + return nil + } + let data = readBuffer.readData(length: length) + guard let string = String(data: data, encoding: .utf8) else { + return nil + } + return .builtin(string) + case 1: + if readBuffer.offset + 8 > readBuffer.length { + return nil + } + var fileId: Int64 = 0 + readBuffer.read(&fileId, offset: 0, length: 8) + return .custom(fileId) + default: + return nil + } + } + public struct RecentPeer: Equatable, PostboxCoding { public var value: MessageReaction.Reaction public var isLarge: Bool @@ -221,6 +306,18 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { return result } + public var customTags: [MemoryBuffer] { + if self.isTags { + var result: [MemoryBuffer] = [] + for reaction in self.reactions { + result.append(ReactionsMessageAttribute.messageTag(reaction: reaction.value)) + } + return result + } else { + return [] + } + } + public init(canViewList: Bool, isTags: Bool, reactions: [MessageReaction], recentPeers: [RecentPeer]) { self.canViewList = canViewList self.isTags = isTags diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift index 84b2dbebed..39c1df9b28 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/MessagesData.swift @@ -319,7 +319,38 @@ public extension TelegramEngine.EngineData.Item { } var key: PostboxViewKey { - return .historyTagSummaryView(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: Namespaces.Message.Cloud) + return .historyTagSummaryView(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: Namespaces.Message.Cloud, customTag: nil) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? MessageHistoryTagSummaryView else { + preconditionFailure() + } + return view.count.flatMap(Int.init) + } + } + + public struct ReactionTagMessageCount: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public struct ItemKey: Hashable { + public var peerId: EnginePeer.Id + public var reaction: MessageReaction.Reaction + } + + public typealias Result = Int? + + fileprivate var peerId: EnginePeer.Id + fileprivate var reaction: MessageReaction.Reaction + public var mapKey: ItemKey { + return ItemKey(peerId: self.peerId, reaction: self.reaction) + } + + public init(peerId: EnginePeer.Id, reaction: MessageReaction.Reaction) { + self.peerId = peerId + self.reaction = reaction + } + + var key: PostboxViewKey { + return .historyTagSummaryView(tag: [], peerId: self.peerId, threadId: nil, namespace: Namespaces.Message.Cloud, customTag: ReactionsMessageAttribute.messageTag(reaction: self.reaction)) } func extract(view: PostboxView) -> Result { @@ -356,5 +387,32 @@ public extension TelegramEngine.EngineData.Item { return EngineMessage(message) } } + + public struct SavedMessageTagStats: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = [MessageReaction.Reaction: Int] + + fileprivate var peerId: EnginePeer.Id + + public init(peerId: EnginePeer.Id) { + self.peerId = peerId + } + + var key: PostboxViewKey { + return .historyCustomTagSummariesView(peerId: self.peerId, namespace: Namespaces.Message.Cloud) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? MessageHistoryCustomTagSummariesView else { + preconditionFailure() + } + var result: [MessageReaction.Reaction: Int] = [:] + for (key, value) in view.tags { + if let reaction = ReactionsMessageAttribute.reactionFromMessageTag(tag: key) { + result[reaction] = value + } + } + return result + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index f35211a3e9..3521688df2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -364,6 +364,7 @@ private class AdMessagesHistoryContextImpl { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: author, text: self.text, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index 5fa7cd1cc1..a5295b2a17 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -195,7 +195,7 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo } if !hasUnread && peerId.namespace == Namespaces.Peer.SecretChat { - let unseenSummary = transaction.getMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud) + let unseenSummary = transaction.getMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil) let actionSummary = transaction.getPendingMessageActionsSummary(peerId: peerId, type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud) if (unseenSummary?.count ?? 0) - (actionSummary ?? 0) > 0 { hasUnread = true diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift index f70c294847..83d6301dd5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift @@ -53,7 +53,7 @@ func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: Pe } else { return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: 0, maxId: topId.id) transaction.removeHole(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenPersonalMessage), range: 1 ... (Int32.max - 1)) let ids = transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenPersonalMessage).map({ $0.id }) @@ -120,7 +120,7 @@ func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: P } else { return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: 0, maxId: topId.id) transaction.removeHole(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenReaction), range: 1 ... (Int32.max - 1)) let ids = transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift index b1b6d291cd..680ad775a6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift @@ -10,6 +10,7 @@ public final class EngineMessage: Equatable { public typealias GlobalTags = GlobalMessageTags public typealias LocalTags = LocalMessageTags public typealias ForwardInfo = MessageForwardInfo + public typealias CustomTag = MemoryBuffer private let impl: Message @@ -102,6 +103,7 @@ public final class EngineMessage: Equatable { tags: EngineMessage.Tags, globalTags: EngineMessage.GlobalTags, localTags: EngineMessage.LocalTags, + customTags: [EngineMessage.CustomTag], forwardInfo: EngineMessage.ForwardInfo?, author: EnginePeer?, text: String, @@ -137,6 +139,7 @@ public final class EngineMessage: Equatable { tags: tags, globalTags: globalTags, localTags: localTags, + customTags: customTags, forwardInfo: forwardInfo, author: author?._asPeer(), text: text, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MessageReadStats.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MessageReadStats.swift index 42b8c26ecd..8011bd6fae 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MessageReadStats.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MessageReadStats.swift @@ -15,86 +15,103 @@ public final class MessageReadStats { } func _internal_messageReadStats(account: Account, id: MessageId) -> Signal { - return account.postbox.transaction { transaction -> Api.InputPeer? in - return transaction.getPeer(id.peerId).flatMap(apiInputPeer) + return account.postbox.transaction { transaction -> Peer? in + return transaction.getPeer(id.peerId) } - |> mapToSignal { inputPeer -> Signal in - guard let inputPeer = inputPeer else { + |> mapToSignal { peer -> Signal in + guard let peer, let inputPeer = apiInputPeer(peer) else { return .single(nil) } if id.namespace != Namespaces.Message.Cloud { return .single(nil) } - - let readPeers: Signal<[(Int64, Int32)]?, NoError> = account.network.request(Api.functions.messages.getMessageReadParticipants(peer: inputPeer, msgId: id.id)) - |> map { result -> [(Int64, Int32)]? in - var items: [(Int64, Int32)] = [] - for item in result { - switch item { - case let .readParticipantDate(userId, date): - items.append((userId, date)) + + if id.peerId.namespace == Namespaces.Peer.CloudUser { + return account.network.request(Api.functions.messages.getOutboxReadDate(peer: inputPeer, msgId: id.id)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> MessageReadStats? in + guard let result else { + return MessageReadStats(reactionCount: 0, peers: [], readTimestamps: [:]) + } + switch result { + case let .outboxReadDate(date): + return MessageReadStats(reactionCount: 0, peers: [EnginePeer(peer)], readTimestamps: [peer.id: date]) } } - return items - } - |> `catch` { _ -> Signal<[(Int64, Int32)]?, NoError> in - return .single(nil) - } - - let reactionCount: Signal = account.network.request(Api.functions.messages.getMessageReactionsList(flags: 0, peer: inputPeer, id: id.id, reaction: nil, offset: nil, limit: 1)) - |> map { result -> Int in - switch result { - case let .messageReactionsList(_, count, _, _, _, _): - return Int(count) - } - } - |> `catch` { _ -> Signal in - return .single(0) - } - - return combineLatest(readPeers, reactionCount) - |> mapToSignal { result, reactionCount -> Signal in - return account.postbox.transaction { transaction -> (peerIds: [PeerId], readTimestamps: [PeerId: Int32], missingPeerIds: [PeerId]) in - var peerIds: [PeerId] = [] - var readTimestamps: [PeerId: Int32] = [:] - var missingPeerIds: [PeerId] = [] - - let authorId = transaction.getMessage(id)?.author?.id - - if let result = result { - for (id, timestamp) in result { - let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)) - readTimestamps[peerId] = timestamp - if peerId == account.peerId { - continue - } - if peerId == authorId { - continue - } - peerIds.append(peerId) - if transaction.getPeer(peerId) == nil { - missingPeerIds.append(peerId) - } + } else { + let readPeers: Signal<[(Int64, Int32)]?, NoError> = account.network.request(Api.functions.messages.getMessageReadParticipants(peer: inputPeer, msgId: id.id)) + |> map { result -> [(Int64, Int32)]? in + var items: [(Int64, Int32)] = [] + for item in result { + switch item { + case let .readParticipantDate(userId, date): + items.append((userId, date)) } } - - return (peerIds: peerIds, readTimestamps: readTimestamps, missingPeerIds: missingPeerIds) + return items } - |> mapToSignal { peerIds, readTimestamps, missingPeerIds -> Signal in - if missingPeerIds.isEmpty || id.peerId.namespace != Namespaces.Peer.CloudChannel { - return account.postbox.transaction { transaction -> MessageReadStats? in - return MessageReadStats(reactionCount: reactionCount, peers: peerIds.compactMap { peerId -> EnginePeer? in - return transaction.getPeer(peerId).flatMap(EnginePeer.init) - }, readTimestamps: readTimestamps) + |> `catch` { _ -> Signal<[(Int64, Int32)]?, NoError> in + return .single(nil) + } + + let reactionCount: Signal = account.network.request(Api.functions.messages.getMessageReactionsList(flags: 0, peer: inputPeer, id: id.id, reaction: nil, offset: nil, limit: 1)) + |> map { result -> Int in + switch result { + case let .messageReactionsList(_, count, _, _, _, _): + return Int(count) + } + } + |> `catch` { _ -> Signal in + return .single(0) + } + + return combineLatest(readPeers, reactionCount) + |> mapToSignal { result, reactionCount -> Signal in + return account.postbox.transaction { transaction -> (peerIds: [PeerId], readTimestamps: [PeerId: Int32], missingPeerIds: [PeerId]) in + var peerIds: [PeerId] = [] + var readTimestamps: [PeerId: Int32] = [:] + var missingPeerIds: [PeerId] = [] + + let authorId = transaction.getMessage(id)?.author?.id + + if let result = result { + for (id, timestamp) in result { + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)) + readTimestamps[peerId] = timestamp + if peerId == account.peerId { + continue + } + if peerId == authorId { + continue + } + peerIds.append(peerId) + if transaction.getPeer(peerId) == nil { + missingPeerIds.append(peerId) + } + } } - } else { - return _internal_channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: id.peerId, category: .recent(.all), offset: 0, limit: 50, hash: 0) - |> mapToSignal { _ -> Signal in + + return (peerIds: peerIds, readTimestamps: readTimestamps, missingPeerIds: missingPeerIds) + } + |> mapToSignal { peerIds, readTimestamps, missingPeerIds -> Signal in + if missingPeerIds.isEmpty || id.peerId.namespace != Namespaces.Peer.CloudChannel { return account.postbox.transaction { transaction -> MessageReadStats? in return MessageReadStats(reactionCount: reactionCount, peers: peerIds.compactMap { peerId -> EnginePeer? in return transaction.getPeer(peerId).flatMap(EnginePeer.init) }, readTimestamps: readTimestamps) } + } else { + return _internal_channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: id.peerId, category: .recent(.all), offset: 0, limit: 50, hash: 0) + |> mapToSignal { _ -> Signal in + return account.postbox.transaction { transaction -> MessageReadStats? in + return MessageReadStats(reactionCount: reactionCount, peers: peerIds.compactMap { peerId -> EnginePeer? in + return transaction.getPeer(peerId).flatMap(EnginePeer.init) + }, readTimestamps: readTimestamps) + } + } } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index f90a55d121..18d4643b52 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -479,9 +479,9 @@ public extension TelegramEngine { maxId = max(maxId, message.id.id) return true }) - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: Int32(localCount), maxId: maxId) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, customTag: nil, count: Int32(localCount), maxId: maxId) } else { - transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: count, maxId: maxId ?? 1) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, customTag: nil, count: count, maxId: maxId ?? 1) } } } @@ -694,6 +694,11 @@ public extension TelegramEngine { |> ignoreValues } + public func keepMessageCountersSyncrhonized(peerId: EnginePeer.Id) -> Signal { + return managedSynchronizeMessageHistoryTagSummaries(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, threadId: nil) + |> ignoreValues + } + public func getSynchronizeAutosaveItemOperations() -> Signal<[(index: Int32, message: Message, mediaId: MediaId)], NoError> { return self.account.postbox.transaction { transaction -> [(index: Int32, message: Message, mediaId: MediaId)] in return _internal_getSynchronizeAutosaveItemOperations(transaction: transaction) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift index 0788e8f6d9..76e4cee9f5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift @@ -791,8 +791,8 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]]) - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) - transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: unreadMentionsCount, maxId: topMessage) + transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: unreadReactionsCount, maxId: topMessage) if let pts = pts { if transaction.getPeerChatState(peerId) == nil { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/TelegramEnginePrivacy.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/TelegramEnginePrivacy.swift index 28dccb7329..93d8d19d1b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/TelegramEnginePrivacy.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/TelegramEnginePrivacy.swift @@ -37,6 +37,10 @@ public extension TelegramEngine { return _internal_updateAccountAutoArchiveChats(account: self.account, value: value) } + public func updateNonContactChatsRequirePremium(value: Bool) -> Signal { + return _internal_updateNonContactChatsRequirePremium(account: self.account, value: value) + } + public func updateAccountKeepArchivedFolders(value: Bool) -> Signal { return _internal_updateAccountKeepArchivedFolders(account: self.account, value: value) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index e0609dc2eb..87e3c6d59b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -20,10 +20,15 @@ func _internal_updateGlobalPrivacySettings(account: Account) -> Signal Signal S } } +func _internal_updateNonContactChatsRequirePremium(account: Account, value: Bool) -> Signal { + return account.postbox.transaction { transaction -> GlobalPrivacySettings in + return fetchGlobalPrivacySettings(transaction: transaction) + } + |> mapToSignal { settings -> Signal in + var settings = settings + settings.nonContactChatsRequirePremium = value + return _internal_updateGlobalPrivacySettings(account: account, settings: settings) + } +} + func _internal_updateAccountKeepArchivedFolders(account: Account, value: Bool) -> Signal { return account.postbox.transaction { transaction -> GlobalPrivacySettings in return fetchGlobalPrivacySettings(transaction: transaction) @@ -276,6 +296,13 @@ func _internal_updateGlobalPrivacySettings(account: Account, settings: GlobalPri if settings.keepArchivedFolders { flags |= 1 << 2 } + if settings.hideReadTime { + flags |= 1 << 3 + } + if settings.nonContactChatsRequirePremium { + flags |= 1 << 4 + } + return account.network.request(Api.functions.account.setGlobalPrivacySettings( settings: .globalPrivacySettings(flags: flags) )) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 8374142db5..7011acd75e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -110,23 +110,33 @@ public extension TelegramEngine { } public func savedMessageTags() -> Signal<([SavedMessageTags.Tag], [Int64: TelegramMediaFile]), NoError> { - return self.account.postbox.transaction { transaction -> ([SavedMessageTags.Tag], [Int64: TelegramMediaFile]) in - guard let savedMessageTags = _internal_savedMessageTags(transaction: transaction) else { - return ([], [:]) + return self.account.postbox.combinedView(keys: [PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())]) + |> mapToSignal { views -> Signal<([SavedMessageTags.Tag], [Int64: TelegramMediaFile]), NoError> in + guard let views = views.views[PostboxViewKey.cachedItem(_internal_savedMessageTagsCacheKey())] as? CachedItemView else { + return .single(([], [:])) } - var files: [Int64: TelegramMediaFile] = [:] - for tag in savedMessageTags.tags { - if case let .custom(fileId) = tag.reaction { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { - files[fileId] = file + guard let savedMessageTags = views.value?.get(SavedMessageTags.self) else { + return .single(([], [:])) + } + return self.account.postbox.transaction { transaction -> ([SavedMessageTags.Tag], [Int64: TelegramMediaFile]) in + var files: [Int64: TelegramMediaFile] = [:] + for tag in savedMessageTags.tags { + if case let .custom(fileId) = tag.reaction { + let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) + if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { + files[fileId] = file + } } } + return (savedMessageTags.tags, files) } - return (savedMessageTags.tags, files) } } + public func refreshSavedMessageTags() -> Signal { + return managedSynchronizeSavedMessageTags(postbox: self.account.postbox, network: self.account.network) + } + public func emojiSearchCategories(kind: EmojiSearchCategories.Kind) -> Signal { return _internal_cachedEmojiSearchCategories(postbox: self.account.postbox, kind: kind) } diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 4048243992..eef13c32b0 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -234,7 +234,7 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ let second = UInt32(hashValue & 0xffffffff) let stableId = first &+ second - return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) + return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) } func locallyRenderedMessage(message: StoreMessage, peers: AccumulatedPeers, associatedThreadInfo: Message.AssociatedThreadInfo? = nil) -> Message? { @@ -290,7 +290,7 @@ func locallyRenderedMessage(message: StoreMessage, peers: AccumulatedPeers, asso let second = UInt32(hashValue & 0xffffffff) let stableId = first &+ second - return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) + return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: message.customTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) } public extension Message { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 33e80d1fca..e5b2bfb4ab 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -804,7 +804,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { animationFileId: animationFileId ), count: Int(reaction.count), - peers: peers, + peers: arguments.areReactionsTags ? [] : peers, chosenOrder: reaction.chosenOrder ) }, diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index dfb8fb018f..e5b988c545 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -133,7 +133,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { peers[peer.id] = peer 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAbout(prev, new): var peers = SimpleDictionary() @@ -164,14 +164,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { }, to: &text, entities: &entities) } 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) 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: [], forwardInfo: nil, author: author, text: prev, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) } case let .changeUsername(prev, new): @@ -202,7 +202,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { }, to: &text, entities: &entities) } 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var previousAttributes: [MessageAttribute] = [] @@ -220,8 +220,8 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) } - 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: [], 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: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let prevMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) } case let .changeUsernames(prev, new): @@ -252,7 +252,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { }, to: &text, entities: &entities) } 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var previousAttributes: [MessageAttribute] = [] @@ -289,8 +289,8 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) } - 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: [], 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: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let prevMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) } case let .changePhoto(_, new): @@ -309,7 +309,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleInvites(value): var peers = SimpleDictionary() @@ -336,7 +336,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleSignatures(value): var peers = SimpleDictionary() @@ -363,7 +363,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updatePinned(message): switch self.id.contentIndex { @@ -394,7 +394,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.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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: if let message = message { @@ -412,7 +412,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: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.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: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } else { var peers = SimpleDictionary() @@ -434,7 +434,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } @@ -479,7 +479,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() @@ -496,7 +496,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: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.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: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) } case let .deleteMessage(message): @@ -522,7 +522,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() @@ -549,7 +549,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { if let peer = self.entry.peers[self.entry.event.peerId] { peers[peer.id] = peer } - let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.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: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case .participantJoin, .participantLeave: @@ -567,7 +567,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } else { 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantInvite(participant): var peers = SimpleDictionary() @@ -584,7 +584,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleBan(prev, new): var peers = SimpleDictionary() @@ -715,7 +715,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: [], forwardInfo: nil, author: author, text: text, attributes: 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: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleAdmin(prev, new): var peers = SimpleDictionary() @@ -952,7 +952,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: [], forwardInfo: nil, author: author, text: text, attributes: 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: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeStickerPack(_, new): var peers = SimpleDictionary() @@ -981,7 +981,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .togglePreHistoryHidden(value): var peers = SimpleDictionary() @@ -1011,7 +1011,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateDefaultBannedRights(prev, new): var peers = SimpleDictionary() @@ -1070,7 +1070,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: [], forwardInfo: nil, author: author, text: text, attributes: 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: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pollStopped(message): switch self.id.contentIndex { @@ -1098,7 +1098,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() @@ -1115,7 +1115,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: [], forwardInfo: nil, author: message.author, text: message.text, attributes: attributes, media: message.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: message.author, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) } case let .linkedPeerUpdated(previous, updated): @@ -1171,7 +1171,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeGeoLocation(_, updated): var peers = SimpleDictionary() @@ -1193,12 +1193,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: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], 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: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .updateSlowmode(_, newValue): @@ -1229,7 +1229,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .startGroupCall, .endGroupCall: var peers = SimpleDictionary() @@ -1266,7 +1266,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantMuteStatus(participantId, isMuted): var peers = SimpleDictionary() @@ -1300,7 +1300,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateGroupCallSettings(joinMuted): var peers = SimpleDictionary() @@ -1329,7 +1329,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantVolume(participantId, volume): var peers = SimpleDictionary() @@ -1360,7 +1360,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteExportedInvitation(invite): var peers = SimpleDictionary() @@ -1386,7 +1386,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .revokeExportedInvitation(invite): var peers = SimpleDictionary() @@ -1412,7 +1412,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .editExportedInvitation(_, updatedInvite): var peers = SimpleDictionary() @@ -1438,7 +1438,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinedViaInvite(invite, joinedViaFolderLink): var peers = SimpleDictionary() @@ -1469,7 +1469,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeHistoryTTL(_, updatedValue): var peers = SimpleDictionary() @@ -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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAvailableReactions(_, updatedValue): var peers = SimpleDictionary() @@ -1571,7 +1571,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeTheme(_, updatedValue): var peers = SimpleDictionary() @@ -1602,7 +1602,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinByRequest(invite, approvedBy): var peers = SimpleDictionary() @@ -1642,7 +1642,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleCopyProtection(value): var peers = SimpleDictionary() @@ -1669,7 +1669,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .sendMessage(message): switch self.id.contentIndex { @@ -1694,7 +1694,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.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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() @@ -1711,7 +1711,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: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.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: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .createTopic(info): @@ -1731,7 +1731,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { return [] }, 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteTopic(info): var peers = SimpleDictionary() @@ -1752,7 +1752,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .editTopic(prevInfo, newInfo): var peers = SimpleDictionary() @@ -1825,7 +1825,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pinTopic(prevInfo, newInfo): var peers = SimpleDictionary() @@ -1863,7 +1863,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleForum(isForum): var peers = SimpleDictionary() @@ -1884,7 +1884,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleAntiSpam(isEnabled): var peers = SimpleDictionary() @@ -1905,7 +1905,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeNameColor(_, _, updatedColor, updatedIcon): var peers = SimpleDictionary() @@ -1974,7 +1974,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeProfileColor(_, _, updatedColor, updatedIcon): var peers = SimpleDictionary() @@ -2053,7 +2053,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeStatus(_, status): var peers = SimpleDictionary() @@ -2088,7 +2088,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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeWallpaper(_, wallpaper): var peers = SimpleDictionary() @@ -2116,7 +2116,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { 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: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], 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: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 5209f3fdf0..b25f6f1339 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -1999,7 +1999,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let gallery = GalleryController(context: strongSelf.context, source: .standaloneMessage(message), streamSingleVideo: true, replaceRootController: { _, _ in }, baseNavigationController: nil) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift index 8e79a99875..2cd86e88ac 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift @@ -23,15 +23,18 @@ public enum PeerInfoPaneKey: Int32 { public struct PeerInfoStatusData: Equatable { public var text: String public var isActivity: Bool + public var isHiddenStatus: Bool public var key: PeerInfoPaneKey? public init( text: String, isActivity: Bool, + isHiddenStatus: Bool = false, key: PeerInfoPaneKey? ) { self.text = text self.isActivity = isActivity + self.isHiddenStatus = isHiddenStatus self.key = key } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index e46761a4d1..6875fbfa71 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -727,7 +727,11 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen if let presence = manager.currentValue { let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 let (text, isActivity) = stringAndActivityForUserPresence(strings: strings, dateTimeFormat: dateTimeFormat, presence: EnginePeer.Presence(presence), relativeTo: Int32(timestamp), expanded: true) - return PeerInfoStatusData(text: text, isActivity: isActivity, key: nil) + var isHiddenStatus = false + if case .hidden = presence.status { + isHiddenStatus = true + } + return PeerInfoStatusData(text: text, isActivity: isActivity, isHiddenStatus: isHiddenStatus, key: nil) } else { return nil } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 06f8967d12..9c8d773377 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -125,6 +125,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var subtitleBackgroundNode: ASDisplayNode? var subtitleBackgroundButton: HighlightTrackingButtonNode? var subtitleArrowNode: ASImageNode? + var subtitleBadgeView: PeerInfoSubtitleBadgeView? let panelSubtitleNode: MultiScaleTextNode let usernameNodeContainer: ASDisplayNode let usernameNodeRawContainer: ASDisplayNode @@ -153,6 +154,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var displayEmojiPackTooltip: (() -> Void)? var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)? + var displayStatusPremiumIntro: (() -> Void)? var navigateToForum: (() -> Void)? @@ -1209,9 +1211,30 @@ final class PeerInfoHeaderNode: ASDisplayNode { let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size + var subtitleBadgeSize: CGSize? let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size + if let statusData, statusData.isHiddenStatus { + let subtitleBadgeView: PeerInfoSubtitleBadgeView + if let current = self.subtitleBadgeView { + subtitleBadgeView = current + } else { + subtitleBadgeView = PeerInfoSubtitleBadgeView(action: { [weak self] in + guard let self else { + return + } + self.displayStatusPremiumIntro?() + }) + self.subtitleBadgeView = subtitleBadgeView + self.subtitleNodeContainer.view.addSubview(subtitleBadgeView) + } + + subtitleBadgeSize = subtitleBadgeView.update(fillColor: contentButtonBackgroundColor, foregroundColor: contentButtonForegroundColor) + } else if let subtitleBadgeView = self.subtitleBadgeView { + subtitleBadgeView.removeFromSuperview() + } + var titleHorizontalOffset: CGFloat = 0.0 var nextIconX: CGFloat = titleSize.width var nextExpandedIconX: CGFloat = titleExpandedSize.width @@ -1272,9 +1295,14 @@ final class PeerInfoHeaderNode: ASDisplayNode { } else { titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) - let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width + var effectiveSubtitleWidth = subtitleSize.width + if let subtitleBadgeSize { + effectiveSubtitleWidth += subtitleBadgeSize.width + 7.0 + } + + let totalSubtitleWidth = effectiveSubtitleWidth + usernameSpacing + usernameSize.width if usernameSize.width == 0.0 { - subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) + subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - effectiveSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize) } else { subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) @@ -1703,6 +1731,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + if let subtitleBadgeView = self.subtitleBadgeView, let subtitleBadgeSize { + let subtitleBadgeFrame = CGRect(origin: CGPoint(x: (subtitleSize.width + 7.0) * 0.5, y: floor((-subtitleBadgeSize.height) * 0.5)), size: subtitleBadgeSize) + transition.updateFrameAdditive(view: subtitleBadgeView, frame: subtitleBadgeFrame) + transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: 1.0 - transitionFraction) + } + let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight) let buttonsTransitionDistanceNorm: CGFloat = 40.0 @@ -2044,6 +2078,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + if let subtitleBadgeView = self.subtitleBadgeView, let result = subtitleBadgeView.hitTest(self.view.convert(point, to: subtitleBadgeView), with: event) { + return result + } + if result.isDescendant(of: self.navigationButtonContainer.view) { return result } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 03747f2e78..07bb7e8e44 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3934,6 +3934,21 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } + self.headerNode.displayStatusPremiumIntro = { [weak self] in + guard let self else { + return + } + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .emojiStatus, action: { + let controller = PremiumIntroScreen(context: context, source: .settings) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.controller?.push(controller) + } + self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in guard let strongSelf = self, let peer = strongSelf.data?.peer else { return @@ -7451,7 +7466,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, openPeer: { _ in }, showAll: false) - let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let controller = LocationViewController(context: context, updatedPresentationData: self.controller?.updatedPresentationData, subject: EngineMessage(message), params: controllerParams) self.controller?.push(controller) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSubtitleBadgeView.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSubtitleBadgeView.swift new file mode 100644 index 0000000000..00bd485533 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSubtitleBadgeView.swift @@ -0,0 +1,91 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import ContextUI +import AnimationUI +import Display +import TelegramPresentationData +import ComponentFlow + +final class PeerInfoSubtitleBadgeView: HighlightTrackingButton { + private let action: () -> Void + + private let backgroundView: BlurredBackgroundView + private let labelView = ComponentView() + + init(action: @escaping () -> Void) { + self.action = action + + self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true) + self.backgroundView.isUserInteractionEnabled = false + + super.init(frame: CGRect()) + + self.addSubview(self.backgroundView) + + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + + self.highligthedChanged = { [weak self] highlighted in + if let self, self.bounds.width > 0.0 { + let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width + let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width + + if highlighted { + self.layer.removeAnimation(forKey: "opacity") + self.layer.removeAnimation(forKey: "sublayerTransform") + self.alpha = 0.7 + let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut) + transition.updateTransformScale(layer: self.layer, scale: topScale) + } else { + self.alpha = 1.0 + self.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) + + let transition: ContainedViewLayoutTransition = .immediate + transition.updateTransformScale(layer: self.layer, scale: 1.0) + + self.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in + guard let self else { + return + } + + self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue) + }) + } + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + self.action() + } + + func update(fillColor: UIColor, foregroundColor: UIColor) -> CGSize { + //TODO:localize + let labelSize = self.labelView.update( + transition: .immediate, + component: AnyComponent(Text(text: "show", font: Font.regular(11.0), color: foregroundColor)), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + + let size = CGSize(width: labelSize.width + 7.0 * 2.0, height: labelSize.height + 4.0 * 2.0) + + self.backgroundView.frame = CGRect(origin: CGPoint(), size: size) + self.backgroundView.updateColor(color: fillColor, transition: .immediate) + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, transition: .immediate) + + if let labelComponentView = self.labelView.view { + if labelComponentView.superview == nil { + labelComponentView.isUserInteractionEnabled = false + self.addSubview(labelComponentView) + } + labelComponentView.frame = CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) * 0.5), y: floor((size.height - labelSize.height) * 0.5)), size: labelSize) + } + + return size + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift index eb6d94202c..b021d2470e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift @@ -719,6 +719,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: nil, text: "", @@ -2129,6 +2130,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: nil, text: "", diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index 90a0f7db35..c6cd207bae 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -227,7 +227,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) if let (_, text) = messageItem.reply { - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[authorPeerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[authorPeerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) } var media: [Media] = [] @@ -235,7 +235,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { media.append(TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: "", displayUrl: "", hash: 0, type: nil, websiteName: site, title: title, text: text, embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil)))) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) } diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift index 6e79e1af54..2e7cca269a 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift @@ -292,7 +292,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { attributes.append(ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: recentPeers)) } - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true, isPreview: true) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true, isPreview: true) var node: ListViewItemNode? if let current = currentNode { diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 52018357b5..14166f5041 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -904,6 +904,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: author, text: text, @@ -1045,20 +1046,20 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate var sampleMessages: [Message] = [] - let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message1) - let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message2) - let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message3) - let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message4.id] = message4 sampleMessages.append(message4) - let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message5.id] = message5 sampleMessages.append(message5) @@ -1066,13 +1067,13 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message7) - let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message8) items = sampleMessages.reversed().map { message in diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift index 2a423e6c83..5b1f2a4ac7 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift @@ -1523,7 +1523,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { messageAuthor = peer._asPeer() let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageAuthor, text: self.presentationData.strings.WallpaperPreview_ChannelReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: messageAuthor, text: self.presentationData.strings.WallpaperPreview_ChannelReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messageAttributes = [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] } @@ -1621,19 +1621,19 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let theme = self.presentationData.theme if !bottomMessageText.isEmpty { - let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) } - let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: messageAttributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: messageAttributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) if let serviceMessageText { let attributedText = convertMarkdownToAttributes(NSAttributedString(string: serviceMessageText)) let entities = generateChatInputTextEntities(attributedText) - let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities, additionalAttributes: nil))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities, additionalAttributes: nil))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index ca36a5d451..29e003b52c 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -4376,6 +4376,7 @@ public final class StoryItemSetContainerComponent: Component { selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(), title: self.displayLikeReactions ? nil : component.strings.Story_SendReactionAsMessage, alwaysAllowPremiumReactions: false, + allPresetReactionsAreAvailable: false, getEmojiContent: { [weak self] animationCache, animationRenderer in guard let self, let component = self.component else { preconditionFailure() diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index e30e658258..5a51351113 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -3295,7 +3295,7 @@ final class StoryItemSetContainerSendMessage { switch mediaArea { case let .venue(_, venue): let action = { [weak controller, weak view] in - let subject = EngineMessage(stableId: 0, stableVersion: 0, id: EngineMessage.Id(peerId: PeerId(0), namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [.geo(TelegramMediaMap(latitude: venue.latitude, longitude: venue.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue.venue, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))], peers: [:], associatedMessages: [:], associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let subject = EngineMessage(stableId: 0, stableVersion: 0, id: EngineMessage.Id(peerId: PeerId(0), namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [.geo(TelegramMediaMap(latitude: venue.latitude, longitude: venue.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue.venue, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))], peers: [:], associatedMessages: [:], associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let locationController = LocationViewController( context: context, updatedPresentationData: updatedPresentationData, diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index 93cd9f7688..1e9a9bbdb4 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -113,6 +113,7 @@ extension ChatControllerImpl { if message.areReactionsTags(accountPeerId: self.context.account.peerId) { //TODO:localize actions.reactionsTitle = "Tag the message with an emoji for quick access later" + actions.allPresetReactionsAreAvailable = true } actions.selectedReactionItems = selectedReactions.reactions @@ -344,29 +345,23 @@ extension ChatControllerImpl { isFirst = !currentReactions.contains(where: { $0.value == chosenReaction }) } - /*guard let allowedReactions = allowedReactions else { - itemNode.openMessageContextMenu() - return - } - - switch allowedReactions { - case let .set(set): - if !messageAlreadyHasThisReaction && updatedReactions.contains(where: { !set.contains($0) }) { - itemNode.openMessageContextMenu() - return - } - case .all: - break - }*/ - - if removedReaction == nil, case .custom = chosenReaction { - if let peer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info { - } else { + if message.areReactionsTags(accountPeerId: self.context.account.peerId) { + if removedReaction == nil, !topReactions.contains(where: { $0.reaction.rawValue == chosenReaction }) { if !self.presentationInterfaceState.isPremium { controller?.premiumReactionsSelected?() return } } + } else { + if removedReaction == nil, case .custom = chosenReaction { + if let peer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info { + } else { + if !self.presentationInterfaceState.isPremium { + controller?.premiumReactionsSelected?() + return + } + } + } } self.chatDisplayNode.historyNode.forEachItemNode { itemNode in diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index bf81b17f52..160bbc9766 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5758,8 +5758,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return .single((message, nil, 0)) } let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: replyThreadId) - let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Cloud) - let localCountViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Local) + let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Cloud, customTag: nil) + let localCountViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: replyThreadId, namespace: Namespaces.Message.Local, customTag: nil) return context.account.postbox.combinedView(keys: [viewKey, countViewKey, localCountViewKey]) |> map { views -> (message: Message?, threadData: MessageHistoryThreadData?, messageCount: Int) in guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { @@ -5791,7 +5791,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let savedMessagesPeerId { let threadPeerId = savedMessagesPeerId let basicPeerKey: PostboxViewKey = .basicPeer(threadPeerId) - let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: savedMessagesPeerId.toInt64(), namespace: Namespaces.Message.Cloud) + let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: savedMessagesPeerId.toInt64(), namespace: Namespaces.Message.Cloud, customTag: nil) savedMessagesPeer = context.account.postbox.combinedView(keys: [basicPeerKey, countViewKey]) |> map { views -> (peer: EnginePeer?, messageCount: Int)? in let peer = ((views.views[basicPeerKey] as? BasicPeerView)?.peer).flatMap(EnginePeer.init) @@ -11409,6 +11409,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if self.keepMessageCountersSyncrhonizedDisposable == nil { self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: message.peerId, threadId: message.threadId).startStrict() } + } else if case .peer(self.context.account.peerId) = self.chatLocation { + if self.keepMessageCountersSyncrhonizedDisposable == nil { + self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: self.context.account.peerId).startStrict() + } } if let scheduledActivateInput = scheduledActivateInput, case .text = scheduledActivateInput { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index f724ecbdc7..0ee493b55f 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -561,6 +561,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: accountPeer, text: options.messageText, diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 6edcc48582..d7cd4e20db 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -72,6 +72,7 @@ func chatHistoryEntriesForView( tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: channelPeer, text: "", @@ -98,6 +99,7 @@ func chatHistoryEntriesForView( tags: [], globalTags: [], localTags: [], + customTags: [], forwardInfo: nil, author: channelPeer, text: "", @@ -428,6 +430,7 @@ func chatHistoryEntriesForView( tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, + customTags: message.customTags, forwardInfo: message.forwardInfo, author: message.author, text: /*"\(message.adAttribute!.opaqueId.hashValue)" + */message.text, diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 2a054b7901..fee26bb610 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -2144,6 +2144,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto tags: initialMessage.tags, globalTags: initialMessage.globalTags, localTags: initialMessage.localTags, + customTags: initialMessage.customTags, forwardInfo: initialMessage.forwardInfo, author: initialMessage.author, text: /*"\(initialMessage.adAttribute!.opaqueId.hashValue)" + */initialMessage.text, diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index ba5d08c8c5..93f5463bba 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -215,8 +215,15 @@ private func canViewReadStats(message: Message, participantCount: Int?, isMessag if let value = data["chat_read_mark_size_threshold"] as? Double { maxParticipantCount = Int(value) } - if let value = data["chat_read_mark_expire_period"] as? Double { - maxTimeout = Int(value) + switch peer { + case _ as TelegramUser: + if let value = data["pm_read_date_expire_period"] as? Double { + maxTimeout = Int(value) + } + default: + if let value = data["chat_read_mark_expire_period"] as? Double { + maxTimeout = Int(value) + } } } @@ -237,6 +244,8 @@ private func canViewReadStats(message: Message, participantCount: Int?, isMessag if group.participantCount > maxParticipantCount { return false } + case _ as TelegramUser: + break default: return false } @@ -1712,6 +1721,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if canViewStats { hasReadReports = true } + } else if let _ = peer as? TelegramUser { + reactionCount = 0 + if canViewStats { + hasReadReports = true + } } else { reactionCount = 0 } @@ -1727,7 +1741,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, hasReadReports: hasReadReports, stats: readStats, action: { c, f, stats, customReactionEmojiPacks, firstCustomEmojiReaction in - if reactionCount == 0, let stats = stats, stats.peers.count == 1, !"".isEmpty { + if message.id.peerId.namespace == Namespaces.Peer.CloudUser { + if let stats, stats.peers.isEmpty { + c.dismiss(completion: { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .emojiStatus, action: { + let controller = PremiumIntroScreen(context: context, source: .settings) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + controllerInteraction.navigationController()?.pushViewController(controller) + }) + } + } else if reactionCount == 0, let stats = stats, stats.peers.count == 1, !"".isEmpty { c.dismiss(completion: { controllerInteraction.openPeer(stats.peers[0], .default, nil, .default) }) @@ -2517,7 +2545,11 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus } if let currentStats = self.currentStats { - self.buttonNode.isUserInteractionEnabled = !currentStats.peers.isEmpty || reactionCount != 0 + if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + self.buttonNode.isUserInteractionEnabled = currentStats.peers.isEmpty + } else { + self.buttonNode.isUserInteractionEnabled = !currentStats.peers.isEmpty || reactionCount != 0 + } } else { self.buttonNode.isUserInteractionEnabled = reactionCount != 0 @@ -2557,7 +2589,11 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus private var validLayout: (calculatedWidth: CGFloat, size: CGSize)? func updateStats(stats: MessageReadStats, transition: ContainedViewLayoutTransition) { - self.buttonNode.isUserInteractionEnabled = !stats.peers.isEmpty || stats.reactionCount != 0 + if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + self.buttonNode.isUserInteractionEnabled = stats.peers.isEmpty + } else { + self.buttonNode.isUserInteractionEnabled = !stats.peers.isEmpty || stats.reactionCount != 0 + } guard let (calculatedWidth, size) = self.validLayout else { return @@ -2590,31 +2626,47 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus reactionCount = currentStats.reactionCount if currentStats.peers.isEmpty { - if reactionCount != 0 { - let text: String = self.presentationData.strings.Chat_ContextReactionCount(Int32(reactionCount)) - self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) + if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + //TODO:localize + self.textNode.attributedText = NSAttributedString(string: "Show Read Time", font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } else { - var text = self.presentationData.strings.Conversation_ContextMenuNoViews - for media in self.item.message.media { - if let file = media as? TelegramMediaFile { - if file.isVoice { - text = self.presentationData.strings.Conversation_ContextMenuNobodyListened - } else if file.isInstantVideo { - text = self.presentationData.strings.Conversation_ContextMenuNobodyWatched + if reactionCount != 0 { + let text: String = self.presentationData.strings.Chat_ContextReactionCount(Int32(reactionCount)) + self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) + } else { + var text = self.presentationData.strings.Conversation_ContextMenuNoViews + for media in self.item.message.media { + if let file = media as? TelegramMediaFile { + if file.isVoice { + text = self.presentationData.strings.Conversation_ContextMenuNobodyListened + } else if file.isInstantVideo { + text = self.presentationData.strings.Conversation_ContextMenuNobodyWatched + } } } + + self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor) } - - self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor) } - }/* else if currentStats.peers.count == 1 { - if reactionCount != 0 { - let text: String = self.presentationData.strings.Chat_OutgoingContextReactionCount(Int32(reactionCount)) - self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) - } else { - self.textNode.attributedText = NSAttributedString(string: currentStats.peers[0].displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) - } - }*/ else { + } else if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser, let timestamp = currentStats.readTimestamps.first?.value { + let dateText = humanReadableStringForTimestamp(strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, timestamp: timestamp, alwaysShowTime: true, allowYesterday: true, format: HumanReadableStringFormat( + dateFormatString: { value in + return PresentationStrings.FormattedString(string: self.presentationData.strings.Chat_MessageSeenTimestamp_Date(value).string, ranges: []) + }, + tomorrowFormatString: { value in + return PresentationStrings.FormattedString(string: self.presentationData.strings.Chat_MessageSeenTimestamp_TodayAt(value).string, ranges: []) + }, + todayFormatString: { value in + return PresentationStrings.FormattedString(string: self.presentationData.strings.Chat_MessageSeenTimestamp_TodayAt(value).string, ranges: []) + }, + yesterdayFormatString: { value in + return PresentationStrings.FormattedString(string: self.presentationData.strings.Chat_MessageSeenTimestamp_YesterdayAt(value).string, ranges: []) + } + )).string + + //TODO:localize + self.textNode.attributedText = NSAttributedString(string: "read \(dateText)", font: Font.regular(floor(self.presentationData.listsFontSize.baseDisplaySize * 0.8)), textColor: self.presentationData.theme.contextMenu.primaryColor) + } else { if reactionCount != 0 { let text: String if reactionCount >= currentStats.peers.count { @@ -2683,7 +2735,8 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus let placeholderAvatarsContent: AnimatedAvatarSetContext.Content var avatarsPeers: [EnginePeer] = [] - if let recentPeers = self.item.message.reactionsAttribute?.recentPeers, !recentPeers.isEmpty { + if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + } else if let recentPeers = self.item.message.reactionsAttribute?.recentPeers, !recentPeers.isEmpty { for recentPeer in recentPeers { if let peer = self.item.message.peers[recentPeer.peerId] { if !avatarsPeers.contains(where: { $0.id == peer.id }) { @@ -2702,7 +2755,12 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus } } avatarsContent = self.avatarsContext.update(peers: avatarsPeers, animated: false) - placeholderAvatarsContent = self.avatarsContext.updatePlaceholder(color: shimmeringForegroundColor, count: 3, animated: false) + + if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { + placeholderAvatarsContent = self.avatarsContext.updatePlaceholder(color: shimmeringForegroundColor, count: 0, animated: false) + } else { + placeholderAvatarsContent = self.avatarsContext.updatePlaceholder(color: shimmeringForegroundColor, count: 3, animated: false) + } let avatarsSize = self.avatarsNode.update(context: self.item.context, content: avatarsContent, itemSize: CGSize(width: 24.0, height: 24.0), customSpacing: 10.0, animated: false, synchronousLoad: true) self.avatarsNode.frame = CGRect(origin: CGPoint(x: size.width - sideInset - 12.0 - avatarsSize.width, y: floor((size.height - avatarsSize.height) / 2.0)), size: avatarsSize) diff --git a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift index d7988cf0a0..c0a3c1a4ce 100644 --- a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift @@ -125,7 +125,7 @@ final class ChatSearchNavigationContentNode: NavigationBarContentNode { let placeholderText = "Search" self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) //TODO:localize - self.searchBar.tokens = [SearchBarToken(id: AnyHashable(tag), context: self.context, icon: nil, isTag: true, emojiFile: file, title: "Tag", permanent: false)] + self.searchBar.tokens = [SearchBarToken(id: AnyHashable(tag), context: self.context, icon: nil, isTag: true, reaction: tag, emojiFile: file, title: "Tag", permanent: false)] case .members: self.searchBar.tokens = [] self.searchBar.prefixString = NSAttributedString(string: strings.Conversation_SearchByName_Prefix, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputTextColor) diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index 8f68249a8e..66d93b79fd 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -56,72 +56,129 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc } } - private final class ItemView: UIView { + private final class ItemView: HighlightTrackingButton { private let context: AccountContext - private let item: Item private let action: () -> Void - private let view = ComponentView() + private let icon = ComponentView() + private let counter = ComponentView() - init(context: AccountContext, item: Item, action: @escaping (() -> Void)) { + init(context: AccountContext, action: @escaping (() -> Void)) { self.context = context - self.item = item self.action = action super.init(frame: CGRect()) + + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + + self.highligthedChanged = { [weak self] highlighted in + if let self, self.bounds.width > 0.0 { + let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width + let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width + + if highlighted { + self.layer.removeAnimation(forKey: "opacity") + self.layer.removeAnimation(forKey: "sublayerTransform") + self.alpha = 0.7 + let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut) + transition.updateTransformScale(layer: self.layer, scale: topScale) + } else { + self.alpha = 1.0 + self.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2) + + let transition: ContainedViewLayoutTransition = .immediate + transition.updateTransformScale(layer: self.layer, scale: 1.0) + + self.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in + guard let self else { + return + } + + self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue) + }) + } + } + } } required init?(coder: NSCoder) { preconditionFailure() } - func update(theme: PresentationTheme, height: CGFloat, transition: Transition) -> CGSize { - let viewSize = self.view.update( - transition: transition, - component: AnyComponent(PlainButtonComponent( - content: AnyComponent(HStack([ - AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiStatusComponent( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - content: .animation( - content: .file(file: self.item.file), - size: CGSize(width: 32.0, height: 32.0), - placeholderColor: theme.list.mediaPlaceholderColor, - themeColor: theme.list.itemPrimaryTextColor, - loopMode: .forever - ), - size: CGSize(width: 16.0, height: 16.0), - isVisibleForAnimations: false, - useSharedAnimation: true, - action: nil, - emojiFileUpdated: nil - ))), - AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "\(self.item.count)", font: Font.regular(15.0), textColor: theme.rootController.navigationBar.secondaryTextColor)) - ))) - ], spacing: 4.0)), - effectAlignment: .center, - minSize: CGSize(width: 0.0, height: height), - contentInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), - action: { [weak self] in - guard let self else { - return - } - self.action() - }, - isEnabled: true + @objc private func pressed() { + self.action() + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + var mappedPoint = point + if self.bounds.insetBy(dx: -8.0, dy: -4.0).contains(point) { + mappedPoint = self.bounds.center + } + return super.hitTest(mappedPoint, with: event) + } + + func update(item: Item, theme: PresentationTheme, height: CGFloat, transition: Transition) -> CGSize { + let spacing: CGFloat = 4.0 + + let reactionSize = CGSize(width: 16.0, height: 16.0) + var reactionDisplaySize = reactionSize + if case .builtin = item.reaction { + reactionDisplaySize = CGSize(width: reactionDisplaySize.width * 2.0, height: reactionDisplaySize.height * 2.0) + } + + let _ = self.icon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: self.context, + animationCache: self.context.animationCache, + animationRenderer: self.context.animationRenderer, + content: .animation( + content: .file(file: item.file), + size: reactionDisplaySize, + placeholderColor: theme.list.mediaPlaceholderColor, + themeColor: theme.list.itemPrimaryTextColor, + loopMode: .forever + ), + isVisibleForAnimations: false, + useSharedAnimation: true, + action: nil, + emojiFileUpdated: nil )), environment: {}, - containerSize: CGSize(width: 100.0, height: 32.0) + containerSize: reactionDisplaySize ) - if let componentView = self.view.view { - if componentView.superview == nil { - self.addSubview(componentView) + + let counterSize = self.counter.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: "\(item.count)", font: Font.regular(14.0), textColor: theme.rootController.navigationBar.secondaryTextColor)) + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + + let size = CGSize(width: reactionSize.width + spacing + counterSize.width, height: height) + + let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - reactionSize.height) * 0.5)), size: reactionSize) + let counterFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: floor((size.height - counterSize.height) * 0.5)), size: counterSize) + + if let iconView = self.icon.view { + if iconView.superview == nil { + iconView.isUserInteractionEnabled = false + self.addSubview(iconView) } - transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(), size: viewSize)) + iconView.frame = reactionDisplaySize.centered(around: iconFrame.center) } - return viewSize + + if let counterView = self.counter.view { + if counterView.superview == nil { + counterView.isUserInteractionEnabled = false + self.addSubview(counterView) + } + counterView.frame = counterFrame + } + + return size } } @@ -171,23 +228,66 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc self.scrollView.disablesInteractiveTransitionGestureRecognizer = true - self.itemsDisposable = (context.engine.stickers.savedMessageTags() - |> deliverOnMainQueue).start(next: { [weak self] tags, files in + let tagsAndFiles: Signal<([MessageReaction.Reaction: Int], [Int64: TelegramMediaFile]), NoError> = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId) + ) + |> distinctUntilChanged + |> mapToSignal { tags -> Signal<([MessageReaction.Reaction: Int], [Int64: TelegramMediaFile]), NoError> in + var customFileIds: [Int64] = [] + for (reaction, _) in tags { + switch reaction { + case .builtin: + break + case let .custom(fileId): + customFileIds.append(fileId) + } + } + + return context.engine.stickers.resolveInlineStickers(fileIds: customFileIds) + |> map { files in + return (tags, files) + } + } + + var isFirstUpdate = true + self.itemsDisposable = (combineLatest( + context.engine.stickers.availableReactions(), + tagsAndFiles + ) + |> deliverOnMainQueue).start(next: { [weak self] availableReactions, tagsAndFiles in guard let self else { return } - self.items = tags.compactMap { tag -> Item? in - switch tag.reaction { + self.items.removeAll() + + let (tags, files) = tagsAndFiles + for (reaction, count) in tags { + switch reaction { case .builtin: - return nil - case let .custom(fileId): - guard let file = files[fileId] else { - return nil + if let availableReactions { + inner: for availableReaction in availableReactions.reactions { + if availableReaction.value == reaction { + if let file = availableReaction.centerAnimation { + self.items.append(Item(reaction: reaction, count: count, file: file)) + } + break inner + } + } + } + case let .custom(fileId): + if let file = files[fileId] { + self.items.append(Item(reaction: reaction, count: count, file: file)) } - return Item(reaction: tag.reaction, count: tag.count, file: file) } } - self.update(transition: .immediate) + self.items.sort(by: { lhs, rhs in + if lhs.count != rhs.count { + return lhs.count > rhs.count + } + return lhs.reaction < rhs.reaction + }) + self.update(transition: isFirstUpdate ? .immediate : .animated(duration: 0.3, curve: .easeInOut)) + isFirstUpdate = false }) } @@ -216,8 +316,8 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc private func update(params: Params, transition: ContainedViewLayoutTransition) { let panelHeight: CGFloat = 33.0 - let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 2.0, bottom: 0.0, right: params.rightInset + 2.0) - let itemSpacing: CGFloat = 2.0 + let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 14.0, bottom: 0.0, right: params.rightInset + 14.0) + let itemSpacing: CGFloat = 20.0 var contentSize = CGSize(width: 0.0, height: panelHeight) contentSize.width += containerInsets.left @@ -233,11 +333,15 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc let itemId = item.reaction validIds.append(itemId) + var itemTransition = transition + var animateIn = false let itemView: ItemView if let current = self.itemViews[itemId] { itemView = current } else { - itemView = ItemView(context: self.context, item: item, action: { [weak self] in + itemTransition = .immediate + animateIn = true + itemView = ItemView(context: self.context, action: { [weak self] in guard let self else { return } @@ -247,15 +351,32 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc self.scrollView.addSubview(itemView) } - let itemSize = itemView.update(theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) - itemView.frame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: itemSize) + let itemSize = itemView.update(item: item, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) + let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: itemSize) + + itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center) + if animateIn && transition.isAnimated { + itemView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + transition.animateTransformScale(view: itemView, from: 0.001) + } + + itemView.bounds = CGRect(origin: CGPoint(), size: itemFrame.size) + contentSize.width += itemSize.width } var removedIds: [MessageReaction.Reaction] = [] for (id, itemView) in self.itemViews { if !validIds.contains(id) { removedIds.append(id) - itemView.removeFromSuperview() + + if transition.isAnimated { + itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak itemView] _ in + itemView?.removeFromSuperview() + }) + transition.updateTransformScale(layer: itemView.layer, scale: 0.001) + } else { + itemView.removeFromSuperview() + } } } for id in removedIds { From 52334868ad847cc9ea787e0a3b834591527f8402 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 6 Jan 2024 01:02:11 +0400 Subject: [PATCH 3/6] Fix isTags --- .../Sources/ApiUtils/ReactionsMessageAttribute.swift | 8 ++++---- .../TelegramCore/Sources/State/MessageReactions.swift | 6 +++--- submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 8 ++++---- .../Sources/ChatMessageAnimatedStickerItemNode.swift | 4 ++-- .../Sources/ChatMessageAttachedContentNode.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 6 +++--- .../Sources/ChatMessageContactBubbleContentNode.swift | 2 +- .../Sources/ChatMessageGiveawayBubbleContentNode.swift | 2 +- .../Sources/ChatMessageInstantVideoItemNode.swift | 2 +- .../Sources/ChatMessageInteractiveFileNode.swift | 2 +- .../Sources/ChatMessageInteractiveInstantVideoNode.swift | 2 +- .../Sources/ChatMessageMapBubbleContentNode.swift | 2 +- .../Sources/ChatMessageMediaBubbleContentNode.swift | 2 +- .../Sources/ChatMessagePollBubbleContentNode.swift | 2 +- .../Sources/ChatMessageReactionsFooterContentNode.swift | 2 +- .../Sources/ChatMessageRestrictedBubbleContentNode.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 4 ++-- .../Sources/ChatMessageTextBubbleContentNode.swift | 2 +- .../Chat/ChatControllerOpenMessageContextMenu.swift | 2 +- submodules/TelegramUI/Sources/ChatController.swift | 8 ++++---- .../Sources/ChatInterfaceStateContextMenus.swift | 8 ++++---- submodules/TelegramUI/Sources/TopMessageReactions.swift | 2 +- 22 files changed, 40 insertions(+), 40 deletions(-) diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift index 5a69a0a61a..cb98e12f44 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift @@ -59,8 +59,8 @@ extension ReactionsMessageAttribute { } } -public func mergedMessageReactionsAndPeers(accountPeer: EnginePeer?, message: Message) -> (reactions: [MessageReaction], peers: [(MessageReaction.Reaction, EnginePeer)]) { - guard let attribute = mergedMessageReactions(attributes: message.attributes) else { +public func mergedMessageReactionsAndPeers(accountPeerId: EnginePeer.Id, accountPeer: EnginePeer?, message: Message) -> (reactions: [MessageReaction], peers: [(MessageReaction.Reaction, EnginePeer)]) { + guard let attribute = mergedMessageReactions(attributes: message.attributes, isTags: message.areReactionsTags(accountPeerId: accountPeerId)) else { return ([], []) } @@ -150,7 +150,7 @@ private func mergeReactions(reactions: [MessageReaction], recentPeers: [Reaction return (result, recentPeers) } -public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsMessageAttribute? { +public func mergedMessageReactions(attributes: [MessageAttribute], isTags: Bool) -> ReactionsMessageAttribute? { var current: ReactionsMessageAttribute? var pending: PendingReactionsMessageAttribute? for attribute in attributes { @@ -170,7 +170,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM recentPeers = updatedRecentPeers if !reactions.isEmpty { - return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, isTags: current?.isTags ?? false, reactions: reactions, recentPeers: recentPeers) + return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, isTags: current?.isTags ?? isTags, reactions: reactions, recentPeers: recentPeers) } else { return nil } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 9f87c2e72a..c23296feef 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -67,7 +67,7 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes } if storeAsRecentlyUsed { - let effectiveReactions = currentMessage.effectiveReactions ?? [] + let effectiveReactions = currentMessage.effectiveReactions(isTags: currentMessage.areReactionsTags(accountPeerId: account.peerId)) ?? [] for updatedReaction in reactions { if !effectiveReactions.contains(where: { $0.value == updatedReaction.reaction && $0.isSelected }) { let recentReactionItem: RecentReactionItem @@ -92,7 +92,7 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes var mappedReactions = mappedReactions - let updatedReactions = mergedMessageReactions(attributes: attributes + [PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge, storeAsRecentlyUsed: storeAsRecentlyUsed)])?.reactions ?? [] + let updatedReactions = mergedMessageReactions(attributes: attributes + [PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge, storeAsRecentlyUsed: storeAsRecentlyUsed)], isTags: currentMessage.areReactionsTags(accountPeerId: account.peerId))?.reactions ?? [] let updatedOutgoingReactions = updatedReactions.filter(\.isSelected) if updatedOutgoingReactions.count > maxCount { let sortedOutgoingReactions = updatedOutgoingReactions.sorted(by: { $0.chosenOrder! < $1.chosenOrder! }) @@ -171,7 +171,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) } - let reactions = mergedMessageReactions(attributes: currentMessage.attributes) + let reactions = mergedMessageReactions(attributes: currentMessage.attributes, isTags: currentMessage.areReactionsTags(accountPeerId: stateManager.accountPeerId)) var attributes = currentMessage.attributes for j in (0 ..< attributes.count).reversed() { if attributes[j] is PendingReactionsMessageAttribute || attributes[j] is ReactionsMessageAttribute { diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index eef13c32b0..3863244d27 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -417,23 +417,23 @@ public extension Message { } return nil } - var effectiveReactionsAttribute: ReactionsMessageAttribute? { + func effectiveReactionsAttribute(isTags: Bool) -> ReactionsMessageAttribute? { if !self.hasReactions { return nil } - if let result = mergedMessageReactions(attributes: self.attributes) { + if let result = mergedMessageReactions(attributes: self.attributes, isTags: isTags) { return result } else { return nil } } - var effectiveReactions: [MessageReaction]? { + func effectiveReactions(isTags: Bool) -> [MessageReaction]? { if !self.hasReactions { return nil } - if let result = mergedMessageReactions(attributes: self.attributes) { + if let result = mergedMessageReactions(attributes: self.attributes, isTags: isTags) { return result.reactions } else { return nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index a73458f7c1..458e6b6c51 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1009,7 +1009,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var edited = false var viewCount: Int? = nil var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } @@ -1243,7 +1243,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes, isTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId)) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? if !reactions.reactions.isEmpty { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index e33cf03c17..cc4c9ecd40 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -592,7 +592,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: associatedData.accountPeer, message: message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: associatedData.accountPeer, message: message) if message.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) || presentationData.isPreview { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 9957cb5ae9..4323d3d0da 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -321,7 +321,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } } - if !reactionsAreInline && !hideAllAdditionalInfo, let reactionsAttribute = mergedMessageReactions(attributes: firstMessage.attributes), !reactionsAttribute.reactions.isEmpty { + if !reactionsAreInline && !hideAllAdditionalInfo, let reactionsAttribute = mergedMessageReactions(attributes: firstMessage.attributes, isTags: firstMessage.areReactionsTags(accountPeerId: item.context.account.peerId)), !reactionsAttribute.reactions.isEmpty { if result.last?.1 == ChatMessageTextBubbleContentNode.self { } else { if result.last?.1 == ChatMessagePollBubbleContentNode.self || @@ -1894,7 +1894,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let bubbleReactions: ReactionsMessageAttribute if needReactions { - bubbleReactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) + bubbleReactions = mergedMessageReactions(attributes: item.message.attributes, isTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId)) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { bubbleReactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } @@ -2056,7 +2056,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: message) if message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index b0da36e401..6af722fc3b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -224,7 +224,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index e84182d137..bc3e25fceb 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -457,7 +457,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index 53b1bc1738..52b09d2731 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -597,7 +597,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes, isTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId)) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index abc9fe8f2e..06910acb2c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -883,7 +883,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: arguments.associatedData.accountPeer, message: arguments.topMessage) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: arguments.context.account.peerId, accountPeer: arguments.associatedData.accountPeer, message: arguments.topMessage) if arguments.topMessage.isRestricted(platform: "ios", contentSettings: arguments.context.currentContentSettings.with { $0 }) || arguments.presentationData.isPreview { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 8bb43fc111..5a7fb3719d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -517,7 +517,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let sentViaBot = false var viewCount: Int? = nil var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index 6ceb6ca323..ae765fab0f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -192,7 +192,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift index 789d2c8e45..2be7c5ce22 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift @@ -256,7 +256,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index ee208d3bc7..16a2bc1c90 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -961,7 +961,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift index 38f20827d0..b46df4b98d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift @@ -498,7 +498,7 @@ public final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleConte } return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in - let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) + let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes, isTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId)) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) let buttonsUpdate = buttonsNode.prepareUpdate( context: item.context, presentationData: item.presentationData, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift index 62a1f237de..c97d3af588 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageRestrictedBubbleContentNode/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -56,7 +56,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod var viewCount: Int? var rawText = "" var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index b409e8ae20..f44fe3a17e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -586,7 +586,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { var edited = false var viewCount: Int? = nil var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.message) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } @@ -825,7 +825,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { if shouldDisplayInlineDateReactions(message: item.message, isPremium: item.associatedData.isPremium, forceInline: item.associatedData.forceInlineReactions) { reactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes, isTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId)) ?? ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index bd06bf9063..f08f74aaee 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -224,7 +224,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var dateReplies = 0 - var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeer: item.associatedData.accountPeer, message: item.topMessage) + var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.topMessage) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index 1e9a9bbdb4..bde6218272 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -332,7 +332,7 @@ extension ChatControllerImpl { let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction - let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? [] + let currentReactions = mergedMessageReactions(attributes: message.attributes, isTags: message.areReactionsTags(accountPeerId: self.context.account.peerId))?.reactions ?? [] var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value) var removedReaction: MessageReaction.Reaction? var isFirst = false diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 160bbc9766..ef848d9586 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1477,7 +1477,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var removedReaction: MessageReaction.Reaction? var messageAlreadyHasThisReaction = false - let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? [] + let currentReactions = mergedMessageReactions(attributes: message.attributes, isTags: message.areReactionsTags(accountPeerId: context.account.peerId))?.reactions ?? [] var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value) if let index = updatedReactions.firstIndex(where: { $0 == chosenReaction }) { @@ -1592,7 +1592,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let removedReaction = removedReaction, let targetView = itemNode.targetReactionView(value: removedReaction), shouldDisplayInlineDateReactions(message: message, isPremium: strongSelf.presentationInterfaceState.isPremium, forceInline: false) { var hideRemovedReaction: Bool = false - if let reactions = mergedMessageReactions(attributes: message.attributes) { + if let reactions = mergedMessageReactions(attributes: message.attributes, isTags: message.areReactionsTags(accountPeerId: context.account.peerId)) { for reaction in reactions.reactions { if reaction.value == removedReaction { hideRemovedReaction = reaction.count == 1 @@ -19229,7 +19229,7 @@ func peerMessageAllowedReactions(context: AccountContext, message: Message) -> S |> map { data, availableReactions -> AllowedReactions? in let (peer, allowedReactions) = data - if let effectiveReactions = message.effectiveReactions, effectiveReactions.count >= 11 { + if let effectiveReactions = message.effectiveReactions(isTags: message.areReactionsTags(accountPeerId: context.account.peerId)), effectiveReactions.count >= 11 { return .set(Set(effectiveReactions.map(\.value))) } @@ -19270,7 +19270,7 @@ func peerMessageSelectedReactions(context: AccountContext, message: Message) -> var result = Set() var reactions = Set() - if let effectiveReactions = message.effectiveReactions { + if let effectiveReactions = message.effectiveReactions(isTags: message.areReactionsTags(accountPeerId: context.account.peerId)) { for reaction in effectiveReactions { if !reaction.isSelected { continue diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 93f5463bba..4308acae86 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1698,7 +1698,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState let canViewStats = canViewReadStats(message: message, participantCount: infoSummaryData.participantCount, isMessageRead: isMessageRead, appConfig: appConfig) var reactionCount = 0 - for reaction in mergedMessageReactionsAndPeers(accountPeer: nil, message: message).reactions { + for reaction in mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: nil, message: message).reactions { reactionCount += Int(reaction.count) } if let reactionsAttribute = message.reactionsAttribute { @@ -2487,7 +2487,7 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus var reactionCount = 0 var customEmojiFiles = Set() - for reaction in mergedMessageReactionsAndPeers(accountPeer: nil, message: self.item.message).reactions { + for reaction in mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: nil, message: self.item.message).reactions { reactionCount += Int(reaction.count) if case let .custom(fileId) = reaction.value { @@ -2618,7 +2618,7 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize) var reactionCount = 0 - for reaction in mergedMessageReactionsAndPeers(accountPeer: nil, message: self.item.message).reactions { + for reaction in mergedMessageReactionsAndPeers(accountPeerId: self.item.context.account.peerId, accountPeer: nil, message: self.item.message).reactions { reactionCount += Int(reaction.count) } @@ -2820,7 +2820,7 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus var isActionEnabled: Bool { var reactionCount = 0 - for reaction in mergedMessageReactionsAndPeers(accountPeer: nil, message: self.item.message).reactions { + for reaction in mergedMessageReactionsAndPeers(accountPeerId: self.item.context.account.peerId, accountPeer: nil, message: self.item.message).reactions { reactionCount += Int(reaction.count) } if reactionCount >= 0 { diff --git a/submodules/TelegramUI/Sources/TopMessageReactions.swift b/submodules/TelegramUI/Sources/TopMessageReactions.swift index fb58a030b4..68530d0b43 100644 --- a/submodules/TelegramUI/Sources/TopMessageReactions.swift +++ b/submodules/TelegramUI/Sources/TopMessageReactions.swift @@ -8,7 +8,7 @@ import ReactionSelectionNode func topMessageReactions(context: AccountContext, message: Message) -> Signal<[ReactionItem], NoError> { if message.id.peerId == context.account.peerId { var loadTags = true - if let effectiveReactionsAttribute = message.effectiveReactionsAttribute { + if let effectiveReactionsAttribute = message.effectiveReactionsAttribute(isTags: message.areReactionsTags(accountPeerId: context.account.peerId)) { if !effectiveReactionsAttribute.reactions.isEmpty { if !effectiveReactionsAttribute.isTags { loadTags = false From 46f604c98e80115389c5b7d3b7e2eec1e8fef405 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 6 Jan 2024 01:09:54 +0400 Subject: [PATCH 4/6] Fix navigate to saved messages --- .../Sources/ChatListSearchContainerNode.swift | 8 ++++---- .../Sources/InstantPageControllerNode.swift | 4 ++-- .../FolderInviteLinkListController.swift | 4 ++-- .../Sources/InviteLinkInviteController.swift | 4 ++-- .../Sources/InviteLinkListController.swift | 8 ++++---- .../Sources/InviteLinkViewController.swift | 4 ++-- .../Sources/ChannelStatsController.swift | 4 ++-- .../Sources/PeerInfoScreen.swift | 20 +++++++++---------- ...StoryItemSetContainerViewSendMessage.swift | 4 ++-- .../TelegramUI/Sources/ChatController.swift | 8 ++++---- .../OverlayAudioPlayerController.swift | 4 ++-- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index ab3e61c6f6..5f17988f33 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1421,8 +1421,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } } - (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -1446,8 +1446,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if let self { + (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 6489e5cce0..eb2269ee5a 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -184,8 +184,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { text = "" } } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift index ab449349a4..9cf0cf4827 100644 --- a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift @@ -386,8 +386,8 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese } } - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages { + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, action == .info { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer else { diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift index e6cc1b8796..9b784ac5ae 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift @@ -466,8 +466,8 @@ public final class InviteLinkInviteController: ViewController { } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift index 85d7c95fff..5207eff92b 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift @@ -464,8 +464,8 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio } } - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages { + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, action == .info { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer else { @@ -680,8 +680,8 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio } } - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages { + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, action == .info { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer else { diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index f05988d70d..d6271c8fc6 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -536,8 +536,8 @@ public final class InviteLinkViewController: ViewController { } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 00681b36b5..403492a7e3 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1147,8 +1147,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD } } - presentImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages { + presentImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, action == .info { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in guard let peer else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 07bb7e8e44..81785a6053 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -5468,8 +5468,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -6364,8 +6364,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -7197,8 +7197,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -9153,8 +9153,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -9179,8 +9179,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if let self { + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 5a51351113..5f746112aa 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1097,8 +1097,8 @@ final class StoryItemSetContainerSendMessage { content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: false, - action: { [weak controller] _ in - if savedMessages { + action: { [weak controller] action in + if savedMessages, action == .info { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in guard let controller, let peer else { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index ef848d9586..b7bebfe3e3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2520,8 +2520,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -16521,8 +16521,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift index c28ceaa95c..0ef19eeee8 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift @@ -104,8 +104,8 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer } } - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in - if savedMessages, let self { + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { From 91fa88a4e84b7e6fb061de21c3b53b3fab2979f9 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 6 Jan 2024 01:50:45 +0400 Subject: [PATCH 5/6] Fix call localization --- .../Telegram-iOS/en.lproj/Localizable.strings | 5 +++ .../Sources/Components/StatusView.swift | 38 +++++++++++++------ .../Sources/PrivateCallScreen.swift | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index a1077e05a6..eacceb642e 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10879,5 +10879,10 @@ Sorry for the inconvenience."; "Call.StatusEnded" = "Call Ended"; "Call.StatusMissed" = "Call Missed"; +"Call.WaitingStatusRequesting" = "Requesting"; +"Call.WaitingStatusRinging" = "Ringing"; +"Call.WaitingStatusConnecting" = "Connecting"; +"Call.WaitingStatusReconnecting" = "Reconnecting"; + "Conversation.ContactAddContact" = "ADD"; "Conversation.ContactMessage" = "MESSAGE"; diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift index f2949a4f91..30f287c191 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import ComponentFlow +import TelegramPresentationData private func addRoundedRectPath(context: CGContext, rect: CGRect, radius: CGFloat) { context.saveGState() @@ -154,13 +155,28 @@ private final class SignalStrengthView: UIView { final class StatusView: UIView { private struct LayoutState: Equatable { + var strings: PresentationStrings var state: State var size: CGSize - init(state: State, size: CGSize) { + init(strings: PresentationStrings, state: State, size: CGSize) { + self.strings = strings self.state = state self.size = size } + + static func ==(lhs: LayoutState, rhs: LayoutState) -> Bool { + if lhs.strings !== rhs.strings { + return false + } + if lhs.state != rhs.state { + return false + } + if lhs.size != rhs.size { + return false + } + return true + } } enum WaitingState { @@ -241,12 +257,12 @@ final class StatusView: UIView { self.activeDurationTimer?.invalidate() } - func update(state: State, transition: Transition) -> CGSize { - if let layoutState = self.layoutState, layoutState.state == state { + func update(strings: PresentationStrings, state: State, transition: Transition) -> CGSize { + if let layoutState = self.layoutState, layoutState.strings === strings, layoutState.state == state { return layoutState.size } - let size = self.updateInternal(state: state, transition: transition) - self.layoutState = LayoutState(state: state, size: size) + let size = self.updateInternal(strings: strings, state: state, transition: transition) + self.layoutState = LayoutState(strings: strings, state: state, size: size) self.updateActiveDurationTimer() @@ -268,7 +284,7 @@ final class StatusView: UIView { self.activeDurationTimer = nil if let layoutState = self.layoutState { - let size = self.updateInternal(state: layoutState.state, transition: .immediate) + let size = self.updateInternal(strings: layoutState.strings, state: layoutState.state, transition: .immediate) if layoutState.size != size { self.layoutState = nil self.requestLayout?() @@ -286,7 +302,7 @@ final class StatusView: UIView { } } - private func updateInternal(state: State, transition: Transition) -> CGSize { + private func updateInternal(strings: PresentationStrings, state: State, transition: Transition) -> CGSize { let textString: String var needsDots = false var monospacedDigits = false @@ -297,13 +313,13 @@ final class StatusView: UIView { switch waitingState { case .requesting: - textString = "Requesting" + textString = strings.Call_WaitingStatusRequesting case .ringing: - textString = "Ringing" + textString = strings.Call_WaitingStatusRinging case .connecting: - textString = "Connecting" + textString = strings.Call_WaitingStatusConnecting case .reconnecting: - textString = "Reconnecting" + textString = strings.Call_WaitingStatusReconnecting } case let .active(activeState): monospacedDigits = true diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift index b974b71dc7..aad8c7679c 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift @@ -1298,7 +1298,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu } } - let statusSize = self.statusView.update(state: statusState, transition: .immediate) + let statusSize = self.statusView.update(strings: params.state.strings, state: statusState, transition: .immediate) let titleY: CGFloat if currentAreControlsHidden { From 49cbbc94362159dca0f90d8ea4f9890228f13919 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 6 Jan 2024 01:50:58 +0400 Subject: [PATCH 6/6] Support i420 --- .../Sources/CallControllerNodeV2.swift | 98 ++++++++++++++----- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift index 5fa6b1cd5a..68fd311c24 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift @@ -815,14 +815,53 @@ private final class AdaptedCallVideoSource: VideoSource { } } + final class PixelBufferPool { + let width: Int + let height: Int + let pool: CVPixelBufferPool + + init?(width: Int, height: Int) { + self.width = width + self.height = height + + let bufferOptions: [String: Any] = [ + kCVPixelBufferPoolMinimumBufferCountKey as String: 4 as NSNumber + ] + let pixelBufferOptions: [String: Any] = [ + kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange as NSNumber, + kCVPixelBufferWidthKey as String: width as NSNumber, + kCVPixelBufferHeightKey as String: height as NSNumber, + kCVPixelBufferIOSurfacePropertiesKey as String: [:] as NSDictionary + ] + + var pool: CVPixelBufferPool? + CVPixelBufferPoolCreate(nil, bufferOptions as CFDictionary, pixelBufferOptions as CFDictionary, &pool) + guard let pool else { + return nil + } + self.pool = pool + } + } + + final class PixelBufferPoolState { + var pool: PixelBufferPool? + } + private static let queue = Queue(name: "AdaptedCallVideoSource") private var onUpdatedListeners = Bag<() -> Void>() private(set) var currentOutput: Output? private var textureCache: CVMetalTextureCache? + private var pixelBufferPoolState: QueueLocalObject + private var videoFrameDisposable: Disposable? init(videoStreamSignal: Signal) { + let pixelBufferPoolState = QueueLocalObject(queue: AdaptedCallVideoSource.queue, generate: { + return PixelBufferPoolState() + }) + self.pixelBufferPoolState = pixelBufferPoolState + CVMetalTextureCacheCreate(nil, nil, MetalEngine.shared.device, nil, &self.textureCache) self.videoFrameDisposable = (videoStreamSignal @@ -917,47 +956,62 @@ private final class AdaptedCallVideoSource: VideoSource { sourceId: sourceId ) case let .i420(i420Buffer): + guard let pixelBufferPoolState = pixelBufferPoolState.unsafeGet() else { + return + } + let width = i420Buffer.width let height = i420Buffer.height - /*output = Output( - resolution: CGSize(width: CGFloat(width), height: CGFloat(height)), - textureLayout: .triPlanar(Output.TriPlanarTextureLayout( - y: yTexture, - uv: uvTexture - )), - dataBuffer: Output.NativeDataBuffer(pixelBuffer: nativeBuffer.pixelBuffer), - rotationAngle: rotationAngle, - followsDeviceOrientation: followsDeviceOrientation, - mirrorDirection: mirrorDirection, - sourceId: sourceId - )*/ + let pool: PixelBufferPool? + if let current = pixelBufferPoolState.pool, current.width == width, current.height == height { + pool = current + } else { + pool = PixelBufferPool(width: width, height: height) + pixelBufferPoolState.pool = pool + } + guard let pool else { + return + } - let _ = width - let _ = height - return + let auxAttributes: [String: Any] = [kCVPixelBufferPoolAllocationThresholdKey as String: 5 as NSNumber] + var pixelBuffer: CVPixelBuffer? + let result = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pool.pool, auxAttributes as CFDictionary, &pixelBuffer) + if result == kCVReturnWouldExceedAllocationThreshold { + print("kCVReturnWouldExceedAllocationThreshold, dropping frame") + return + } + guard let pixelBuffer else { + return + } - /*var cvMetalTextureY: CVMetalTexture? - var status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .r8Unorm, width, height, 0, &cvMetalTextureY) + if !copyI420BufferToNV12Buffer(buffer: i420Buffer, pixelBuffer: pixelBuffer) { + return + } + + var cvMetalTextureY: CVMetalTexture? + var status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, .r8Unorm, width, height, 0, &cvMetalTextureY) guard status == kCVReturnSuccess, let yTexture = CVMetalTextureGetTexture(cvMetalTextureY!) else { return } var cvMetalTextureUV: CVMetalTexture? - status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .rg8Unorm, width / 2, height / 2, 1, &cvMetalTextureUV) + status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, .rg8Unorm, width / 2, height / 2, 1, &cvMetalTextureUV) guard status == kCVReturnSuccess, let uvTexture = CVMetalTextureGetTexture(cvMetalTextureUV!) else { return } output = Output( resolution: CGSize(width: CGFloat(yTexture.width), height: CGFloat(yTexture.height)), - y: yTexture, - uv: uvTexture, - dataBuffer: Output.NativeDataBuffer(pixelBuffer: nativeBuffer.pixelBuffer), + textureLayout: .biPlanar(Output.BiPlanarTextureLayout( + y: yTexture, + uv: uvTexture + )), + dataBuffer: Output.NativeDataBuffer(pixelBuffer: pixelBuffer), rotationAngle: rotationAngle, followsDeviceOrientation: followsDeviceOrientation, mirrorDirection: mirrorDirection, sourceId: sourceId - )*/ + ) default: return }