diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..3d54595b0f --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "permissions": { + "allow": [ + "Bash(swift build)", + "Bash(.build/debug/makeproject:*)", + "Bash(mkdir:*)", + "Bash(find:*)", + "Bash(grep:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ed86535e8b..c8f2a8c8b2 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -159,7 +159,7 @@ "moduleExtensions": { "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": { "general": { - "bzlTransitiveDigest": "RjubjYIojbv0PxTpnoknalV9QzT9asbV7elDuN7m2A4=", + "bzlTransitiveDigest": "xcBTf2+GaloFpg7YEh/Bv+1yAczRkiCt3DGws4K7kSk=", "usagesDigest": "lfcV4HxPD+NLaRIT/v7BtSGFgE7c9xrWU7jDiwBAxzo=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index f2928d1157..7949042e9f 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14882,7 +14882,6 @@ Sorry for the inconvenience."; "Stories.ToastAddedToFolder_any" = "%@ stories added to folder."; "Stories.ToastAlbumNotAvailable" = "The album is no longer available."; - "Gift.Options.Gift.Premium" = "premium"; "Stars.Transaction.FragmentTopUpTon.Title" = "TON Top-Up"; @@ -14890,3 +14889,110 @@ Sorry for the inconvenience."; "Stars.Transaction.SearchFee.Title" = "Extra Search Fee"; "Stars.Intro.Transaction.SearchFee" = "Extra Search Fee"; + +"PeerInfo.Gifts.Upgradable" = "Upgradable"; + +"Gift.Upgrade.GiftTitle" = "Make Unique"; +"Gift.Upgrade.GiftDescription" = "Let %@ turn this gift into a unique collectible."; +"Gift.Upgrade.Unique.GiftDescription" = "%@ will get a unique number, model, backdrop, and symbol for the gift."; +"Gift.Upgrade.Transferable.GiftDescription" = "%@ will be able to send the gift to anyone Telegram."; +"Gift.Upgrade.Tradable.GiftDescription" = "%@ will be able to auction the gift on third-party NFT marketplaces."; + +"Notification.StarGift.Subtitle.Upgrade.Prepaid" = "%@ can now turn this gift to a unique collectible"; + +"Notification.StarsGift.SentUpgradeYou" = "You sent an upgrade worth %1$@ for your gift to %2$@"; +"Notification.StarsGift.SentUpgradeYouOther" = "You sent an upgrade worth %1$@ for the gift %2$@ received from %3$@"; +"Notification.StarsGift.SentUpgradeYouUnknown" = "You sent an upgrade worth %1$@ for the gift to %2$@"; + +"Notification.StarsGift.SentUpgrade" = "%1$@ sent an upgrade worth %2$@ for your gift"; +"Notification.StarsGift.SentUpgradeOther" = "%1$@ sent an upgrade worth %2$@ for the gift you received from %3$@"; + +"Notification.StarsGift.UpgradePrepaidYou" = "You unpacked the gift that %@ helped upgrade"; +"Notification.StarsGift.UpgradePrepaid" = "%@ unpacked the gift that you helped upgrade"; + +"Gift.View.Unknown.Title" = "Gift"; + +"Conversation.StopVoiceMessageTitle" = "Audio Recording"; +"Conversation.StopVoiceMessageDescription" = "Are you sure you want to pause recording?"; +"Conversation.StopVoiceMessageDiscardAction" = "Discard"; +"Conversation.StopVoiceMessagePauseAction" = "Pause"; + +"Gift.Options.GiftLocked.Title" = "Gift Locked"; + +"Gift.Unique.Value" = "Value"; +"Gift.Unique.LearnMore" = "learn more"; + +"Gift.Upgrade.GiftUpgrade" = "Pay %@ for Upgrade"; +"Gift.View.GiftUpgrade" = "Gift an Upgrade"; + +"Gift.View.OpenChatTheme" = "This gift is the chat's theme. [Change Theme >]()"; + +"Notification.ChatTheme.Text" = "%1$@ set **%2$@** as a new theme for this chat."; +"Notification.ChatTheme.TextYou" = "You set **%@** as a new theme for this chat."; + +"Notification.ChangedThemeGift" = "%1$@ changed chat theme to %2$@"; +"Notification.YouChangedThemeGift" = "You changed chat theme to %@"; + +"Conversation.Theme.GiftTransfer.Text" = "This gift is already your theme in the chat with **%@**. Remove it there and use it here instead?"; +"Conversation.Theme.GiftTransfer.Proceed" = "Yes"; + +"PeerInfo.Tabs.SetMainTab" = "Set as Main Tab"; +"PeerInfo.Tabs.SetMainTab.Succeed" = "Tab order changed."; + +"MediaPlayer.SavedMusic.AddToProfile" = "Add to Profile"; +"MediaPlayer.SavedMusic.RemoveFromProfile" = "This audio is visible on your profile. [Remove >]()"; + +"MediaPlayer.SavedMusic.AddedToProfile.View" = "View"; +"MediaPlayer.SavedMusic.AddedToProfile" = "Audio added to your profile."; +"MediaPlayer.SavedMusic.RemovedFromProfile" = "Audio removed from your profile."; + +"MediaPlayer.ContextMenu.SaveToFiles" = "Save to Files"; +"MediaPlayer.ContextMenu.SaveTo" = "Save to..."; +"MediaPlayer.ContextMenu.SaveTo.Profile" = "Profile"; +"MediaPlayer.ContextMenu.SaveTo.SavedMessages" = "Saved Messages"; +"MediaPlayer.ContextMenu.SaveTo.Files" = "Files"; +"MediaPlayer.ContextMenu.SaveTo.Info" = "Choose where you want this audio to be saved."; +"MediaPlayer.ContextMenu.ShowInChat" = "Show in Chat"; +"MediaPlayer.ContextMenu.Forward" = "Forward"; +"MediaPlayer.ContextMenu.Delete" = "Delete"; +"MediaPlayer.ContextMenu.Remove" = "Remove"; + +"MediaPlayer.Playlist.ThisChat" = "AUDIO IN THIS CHAT"; +"MediaPlayer.Playlist.SavedMusic" = "%@'S PLAYLIST"; +"MediaPlayer.Playlist.SavedMusicYou" = "YOUR PLAYLIST"; + +"Notification.PremiumGift.Stars_1" = "%@ Star"; +"Notification.PremiumGift.Stars_any" = "%@ Stars"; + +"Ton.ProceedsOverview" = "PROCEEDS OVERVIEW"; +"Ton.AvailableBalance" = "Balance Available to Withdraw"; +"Ton.LifetimeProceeds" = "Total Lifetime Proceeds"; + +"Ton.WithdrawViaFragment" = "Withdraw via Fragment"; +"Ton.WithdrawViaFragment.Info" = "Collect your TON using Fragment. [Learn More >]()"; +"Ton.WithdrawViaFragment.Info_URL" = "https://telegram.org/tos/bot-developers#6-2-2-tpa-balance"; + +"MESSAGE_GIFT_THEME" = "%1$@ changed theme to %2$@"; + +"Gift.Upgrade.Skip" = "Skip"; +"Gift.Upgrade.UpgradeNext" = "Upgrade Next Gift"; + +"Gift.Value.DescriptionAveragePrice" = "This is the average sale price of **%@** on Telegram and Fragment over the past month."; +"Gift.Value.DescriptionLastPriceFragment" = "This is the last price at which **%@** was last sold on Fragment."; +"Gift.Value.DescriptionLastPriceTelegram" = "This is the last price at which **%@** was last sold on Telegram."; + +"Gift.Value.LastPriceInfo" = "**%1$@** is the last price for %2$@ gifts listed on Telegram and Fragment."; +"Gift.Value.MinimumPriceInfo" = "**%1$@** is the floor price for %2$@ gifts listed on Telegram and Fragment."; +"Gift.Value.AveragePriceInfo" = "**%1$@** is the average sale price for %2$@ gifts listed on Telegram and Fragment over the past month."; + +"Gift.Value.AveragePrice" = "Last Sale"; +"Gift.Value.InitialSale" = "Initial Sale"; +"Gift.Value.InitialPrice" = "Initial Price"; +"Gift.Value.LastSale" = "Last Sale"; +"Gift.Value.LastPrice" = "Last Price"; +"Gift.Value.MinimumPrice" = "Minimum Price"; +"Gift.Value.AveragePrice" = "Average Price"; +"Gift.Value.ForSaleOnTelegram" = "for sale on Telegram"; +"Gift.Value.ForSaleOnFragment" = "for sale on Fragment"; + +"Gift.View.Context.SetAsTheme" = "Set as Theme in..."; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index f90c1d0db1..6a36eb689d 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -333,6 +333,7 @@ public enum ResolvedUrl { case shareStory(Int64) case storyFolder(peerId: PeerId, id: Int64) case giftCollection(peerId: PeerId, id: Int64) + case sendGift(peerId: PeerId?) } public enum ResolveUrlResult { @@ -552,7 +553,42 @@ public final class NavigateToChatControllerParams { public let customChatNavigationStack: [EnginePeer.Id]? public let skipAgeVerification: Bool - public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: NavigateToChatControllerParams.ReportReason? = nil, animated: Bool = true, forceAnimatedScroll: Bool = false, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, pushController: ((ChatController, Bool, @escaping () -> Void) -> Void)? = nil, completion: @escaping (ChatController) -> Void = { _ in }, chatListCompletion: @escaping (ChatListController) -> Void = { _ in }, forceOpenChat: Bool = false, customChatNavigationStack: [EnginePeer.Id]? = nil, skipAgeVerification: Bool = false) { + public init( + navigationController: NavigationController, + chatController: ChatController? = nil, + context: AccountContext, + chatLocation: Location, + chatLocationContextHolder: Atomic = Atomic(value: nil), + subject: ChatControllerSubject? = nil, + botStart: ChatControllerInitialBotStart? = nil, + attachBotStart: ChatControllerInitialAttachBotStart? = nil, + botAppStart: ChatControllerInitialBotAppStart? = nil, + updateTextInputState: ChatTextInputState? = nil, + activateInput: ChatControllerActivateInput? = nil, + keepStack: NavigateToChatKeepStack = .default, + useExisting: Bool = true, + useBackAnimation: Bool = false, + purposefulAction: (() -> Void)? = nil, + scrollToEndIfExists: Bool = false, + activateMessageSearch: (ChatSearchDomain, String)? = nil, + peekData: ChatPeekTimeout? = nil, + peerNearbyData: ChatPeerNearbyData? = nil, + reportReason: NavigateToChatControllerParams.ReportReason? = nil, + animated: Bool = true, + forceAnimatedScroll: Bool = false, + options: NavigationAnimationOptions = [], + parentGroupId: PeerGroupId? = nil, + chatListFilter: Int32? = nil, + chatNavigationStack: [ChatNavigationStackItem] = [], + changeColors: Bool = false, + setupController: @escaping (ChatController) -> Void = { _ in }, + pushController: ((ChatController, Bool, @escaping () -> Void) -> Void)? = nil, + completion: @escaping (ChatController) -> Void = { _ in }, + chatListCompletion: @escaping (ChatListController) -> Void = { _ in }, + forceOpenChat: Bool = false, + customChatNavigationStack: [EnginePeer.Id]? = nil, + skipAgeVerification: Bool = false + ) { self.navigationController = navigationController self.chatController = chatController self.chatLocationContextHolder = chatLocationContextHolder @@ -1290,7 +1326,7 @@ public protocol SharedAccountContext: AnyObject { func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController func makeStarsIntroScreen(context: AccountContext) -> ViewController func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: ((StarGift.UniqueGift) -> Void)?) -> ViewController - func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, dismissed: (() -> Void)?) -> ViewController + func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 3fc94c1c06..ccd93181b5 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -800,6 +800,7 @@ public enum ChatControllerSubject: Equatable { case pinnedMessages(id: EngineMessage.Id?) case messageOptions(peerIds: [EnginePeer.Id], ids: [EngineMessage.Id], info: MessageOptionsInfo) case customChatContents(contents: ChatCustomContentsProtocol) + case botForumThread(forumId: EnginePeer.Id, threadId: Int64) public static func ==(lhs: ChatControllerSubject, rhs: ChatControllerSubject) -> Bool { switch lhs { @@ -833,6 +834,12 @@ public enum ChatControllerSubject: Equatable { } else { return false } + case let .botForumThread(forumId, threadId): + if case .botForumThread(forumId, threadId) = rhs { + return true + } else { + return false + } } } @@ -1172,7 +1179,7 @@ public enum ChatHistoryListSource { } case `default` - case custom(messages: Signal<([Message], Int32, Bool), NoError>, messageId: MessageId?, quote: Quote?, loadMore: (() -> Void)?) + case custom(messages: Signal<([Message], Int32, Bool), NoError>, messageId: MessageId?, quote: Quote?, isSavedMusic: Bool, canReorder: Bool, loadMore: (() -> Void)?) case customView(historyView: Signal<(MessageHistoryView, ViewUpdateType), NoError>) } diff --git a/submodules/AccountContext/Sources/MediaManager.swift b/submodules/AccountContext/Sources/MediaManager.swift index 8f12b31b2b..b2a0964af4 100644 --- a/submodules/AccountContext/Sources/MediaManager.swift +++ b/submodules/AccountContext/Sources/MediaManager.swift @@ -12,6 +12,7 @@ public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId { case peer(PeerId) case recentActions(PeerId) case feed(Int32) + case savedMusic(PeerId) case custom public func isEqual(to: SharedMediaPlaylistId) -> Bool { @@ -26,7 +27,8 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation case messages(chatLocation: ChatLocation, tagMask: MessageTags, at: MessageId) case singleMessage(MessageId) case recentActions(Message) - case custom(messages: Signal<([Message], Int32, Bool), NoError>, at: MessageId, loadMore: (() -> Void)?) + case savedMusic(context: ProfileSavedMusicContext, at: Int32, canReorder: Bool) + case custom(messages: Signal<([Message], Int32, Bool), NoError>, canReorder: Bool, at: MessageId, loadMore: (() -> Void)?) public var playlistId: PeerMessagesMediaPlaylistId { switch self { @@ -43,14 +45,55 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation return .peer(id.peerId) case let .recentActions(message): return .recentActions(message.id.peerId) + case let .savedMusic(context, _, _): + return .savedMusic(context.peerId) case .custom: return .custom } } + public func effectiveLocation(context: AccountContext) -> PeerMessagesPlaylistLocation { + switch self { + case let .savedMusic(savedMusicContext, at, canReorder): + let peerId = savedMusicContext.peerId + return .custom( + messages: combineLatest( + savedMusicContext.state, + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: savedMusicContext.peerId)) + ) + |> map { state, peer in + var messages: [Message] = [] + var peers = SimpleDictionary() + if let peer { + peers[peerId] = peer._asPeer() + } + for file in state.files { + let stableId = UInt32(clamping: file.fileId.id % Int64(Int32.max)) + messages.append(Message(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: Int32(stableId)), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [.music], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])) + } + var canLoadMore = false + if case let .ready(canLoadMoreValue) = state.dataState { + canLoadMore = canLoadMoreValue + } + return (messages, Int32(messages.count), canLoadMore) + }, + canReorder: canReorder, + at: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: at), + loadMore: { [weak savedMusicContext] in + guard let savedMusicContext else { + return + } + savedMusicContext.loadMore() + } + ) + default: + return self + } + } + public var messageId: MessageId? { switch self { - case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, messageId, _): + case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, _, messageId, _): return messageId default: return nil @@ -85,8 +128,14 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation } else { return false } - case let .custom(_, lhsAt, _): - if case let .custom(_, rhsAt, _) = rhs, lhsAt == rhsAt { + case let .savedMusic(lhsContext, lhsAt, _): + if case let .savedMusic(rhsContext, rhsAt, _) = rhs { + return lhsContext.peerId == rhsContext.peerId && lhsAt == rhsAt + } else { + return false + } + case let .custom(_, _, lhsAt, _): + if case let .custom(_, _, rhsAt, _) = rhs, lhsAt == rhsAt { return true } else { return false @@ -120,8 +169,10 @@ public func peerMessageMediaPlayerType(_ message: EngineMessage) -> MediaManager return nil } -public func peerMessagesMediaPlaylistAndItemId(_ message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> (SharedMediaPlaylistId, SharedMediaPlaylistItemId)? { - if isGlobalSearch && !isDownloadList { +public func peerMessagesMediaPlaylistAndItemId(_ message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool, isSavedMusic: Bool) -> (SharedMediaPlaylistId, SharedMediaPlaylistItemId)? { + if isSavedMusic { + return (PeerMessagesMediaPlaylistId.savedMusic(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index)) + } else if isGlobalSearch && !isDownloadList { return (PeerMessagesMediaPlaylistId.custom, PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index)) } else if isRecentActions && !isDownloadList { return (PeerMessagesMediaPlaylistId.recentActions(message.id.peerId), PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index)) diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm index e4fad6d308..769dd634fc 100644 --- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm +++ b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm @@ -911,7 +911,7 @@ ASLayoutElementStyleExtensibilityForwarding NSArray *sublayouts = _calculatedDisplayNodeLayout.layout.sublayouts; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif @@ -938,7 +938,7 @@ ASLayoutElementStyleExtensibilityForwarding #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm b/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm index 3902f8853b..9787bc62ce 100644 --- a/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm +++ b/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm @@ -34,7 +34,7 @@ NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm b/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm index 2788e98a85..35be7039ad 100644 --- a/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm +++ b/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm @@ -142,7 +142,7 @@ Class _Nullable ASGetClassFromType(const char * _Nullable type) size_t resultLength = typeLength - 3; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AsyncDisplayKit/Source/ASLayout.mm b/submodules/AsyncDisplayKit/Source/ASLayout.mm index 828f678156..48481c4e60 100644 --- a/submodules/AsyncDisplayKit/Source/ASLayout.mm +++ b/submodules/AsyncDisplayKit/Source/ASLayout.mm @@ -237,7 +237,7 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT( // Fast-reverse-enumerate the sublayouts array by copying it into a C-array and push_front'ing each into the queue. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AsyncDisplayKit/Source/ASLayoutSpec.mm b/submodules/AsyncDisplayKit/Source/ASLayoutSpec.mm index 797f2c8d82..1c99408f2e 100644 --- a/submodules/AsyncDisplayKit/Source/ASLayoutSpec.mm +++ b/submodules/AsyncDisplayKit/Source/ASLayoutSpec.mm @@ -161,7 +161,7 @@ ASLayoutElementStyleExtensibilityForwarding // Use tiny descriptions because these trees can get nested very deep. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif @@ -301,7 +301,7 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__) const auto count = children.count; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AsyncDisplayKit/Source/ASMainThreadDeallocation.mm b/submodules/AsyncDisplayKit/Source/ASMainThreadDeallocation.mm index 35818550a0..e338a975de 100644 --- a/submodules/AsyncDisplayKit/Source/ASMainThreadDeallocation.mm +++ b/submodules/AsyncDisplayKit/Source/ASMainThreadDeallocation.mm @@ -34,7 +34,7 @@ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 180400 #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #endif diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index d0d6924d2c..ff71e9e9cf 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -192,6 +192,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth return } strongSelf.account = updatedAccount + strongSelf.inAppPurchaseManager = InAppPurchaseManager(engine: .unauthorized(strongSelf.engine)) } controller.loginWithNumber = { [weak self, weak controller] number, syncContacts in guard let self else { @@ -217,6 +218,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth case let .sentCode(account): controller?.inProgress = false strongSelf.account = account + strongSelf.inAppPurchaseManager = InAppPurchaseManager(engine: .unauthorized(strongSelf.engine)) case .loggedIn: break } @@ -765,8 +767,8 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth return controller } - private func paymentController(number: String, phoneCodeHash: String, storeProduct: String) -> AuthorizationSequencePaymentScreen { - let controller = AuthorizationSequencePaymentScreen(sharedContext: self.sharedContext, engine: self.engine, presentationData: self.presentationData, inAppPurchaseManager: self.inAppPurchaseManager, phoneNumber: number, phoneCodeHash: phoneCodeHash, storeProduct: storeProduct, back: { [weak self] in + private func paymentController(number: String, phoneCodeHash: String, storeProduct: String, supportEmailAddress: String, supportEmailSubject: String) -> AuthorizationSequencePaymentScreen { + let controller = AuthorizationSequencePaymentScreen(sharedContext: self.sharedContext, engine: self.engine, presentationData: self.presentationData, inAppPurchaseManager: self.inAppPurchaseManager, phoneNumber: number, phoneCodeHash: phoneCodeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject, back: { [weak self] in guard let self else { return } @@ -806,7 +808,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth case .emailNotAllowed: text = strongSelf.presentationData.strings.Login_EmailNotAllowedError } - lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } else { @@ -1303,12 +1305,12 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth } controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel)) self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) - case let .payment(number, codeHash, storeProduct, _): + case let .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, _): var controllers: [ViewController] = [] if !self.otherAccountPhoneNumbers.1.isEmpty { controllers.append(self.splashController()) } - controllers.append(self.paymentController(number: number, phoneCodeHash: codeHash, storeProduct: storeProduct)) + controllers.append(self.paymentController(number: number, phoneCodeHash: codeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject)) self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) } } @@ -1335,7 +1337,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth } } - private static func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) { + static func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) { if MFMailComposeViewController.canSendMail() { final class ComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { @objc func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { @@ -1412,24 +1414,20 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth phoneNumber: String, mnc: String ) { - if MFMailComposeViewController.canSendMail() { - let formattedNumber = formatPhoneNumber(phoneNumber) - - var emailBody = "" - emailBody.append(presentationData.strings.Login_EmailCodeBody(formattedNumber).string) - emailBody.append("\n\n") - - let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" - let systemVersion = UIDevice.current.systemVersion - let locale = Locale.current.identifier - emailBody.append("Telegram: \(appVersion)\n") - emailBody.append("OS: \(systemVersion)\n") - emailBody.append("Locale: \(locale)\n") - emailBody.append("MNC: \(mnc)") - - AuthorizationSequenceController.presentEmailComposeController(address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData) - } else { - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } + let formattedNumber = formatPhoneNumber(phoneNumber) + + var emailBody = "" + emailBody.append(presentationData.strings.Login_EmailCodeBody(formattedNumber).string) + emailBody.append("\n\n") + + let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" + let systemVersion = UIDevice.current.systemVersion + let locale = Locale.current.identifier + emailBody.append("Telegram: \(appVersion)\n") + emailBody.append("OS: \(systemVersion)\n") + emailBody.append("Locale: \(locale)\n") + emailBody.append("MNC: \(mnc)") + + AuthorizationSequenceController.presentEmailComposeController(address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData) } } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift index e410007a36..1f35a72415 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift @@ -22,6 +22,10 @@ import PremiumCoinComponent import Markdown import CountrySelectionUI import AccountContext +import AlertUI +import MessageUI +import CoreTelephony +import PhoneNumberFormat final class AuthorizationSequencePaymentScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -33,6 +37,8 @@ final class AuthorizationSequencePaymentScreenComponent: Component { let phoneNumber: String let phoneCodeHash: String let storeProduct: String + let supportEmailAddress: String + let supportEmailSubject: String init( sharedContext: SharedAccountContext, @@ -41,7 +47,9 @@ final class AuthorizationSequencePaymentScreenComponent: Component { presentationData: PresentationData, phoneNumber: String, phoneCodeHash: String, - storeProduct: String + storeProduct: String, + supportEmailAddress: String, + supportEmailSubject: String ) { self.sharedContext = sharedContext self.engine = engine @@ -50,6 +58,8 @@ final class AuthorizationSequencePaymentScreenComponent: Component { self.phoneNumber = phoneNumber self.phoneCodeHash = phoneCodeHash self.storeProduct = storeProduct + self.supportEmailAddress = supportEmailAddress + self.supportEmailSubject = supportEmailSubject } static func ==(lhs: AuthorizationSequencePaymentScreenComponent, rhs: AuthorizationSequencePaymentScreenComponent) -> Bool { @@ -124,30 +134,55 @@ final class AuthorizationSequencePaymentScreenComponent: Component { self.state?.updated(transition: .immediate) var errorText: String? + var errorCode: Int32? switch error { case .generic: errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + errorCode = 1001 case .network: errorText = presentationData.strings.Premium_Purchase_ErrorNetwork + errorCode = 1002 case .notAllowed: errorText = presentationData.strings.Premium_Purchase_ErrorNotAllowed + errorCode = 1003 case .cantMakePayments: errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments + errorCode = 1004 case .assignFailed: errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + errorCode = 1005 case .tryLater: errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + errorCode = 1006 case .cancelled: break } - if let errorText { - //addAppLogEvent(postbox: component.engine.account.postbox, type: "premium_gift.promo_screen_fail") - - let _ = errorText - let _ = controller - //let alertController = textAlertController(context: component.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - //controller.present(alertController, in: .window(.root)) + if let errorText, let errorCode { + let theme = AlertControllerTheme(presentationData: presentationData) + let alertController = textAlertController( + alertContext: AlertControllerContext(theme: theme, themeSignal: .single(theme)), + title: nil, + text: errorText, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_PhoneNumberHelp, action: { [weak controller] in + guard let controller else { + return + } + let formattedNumber = formatPhoneNumber(component.phoneNumber) + let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" + let systemVersion = UIDevice.current.systemVersion + let locale = Locale.current.identifier + let carrier = CTCarrier() + let mnc = carrier.mobileNetworkCode ?? "none" + let errorString: String = "\(errorCode): \(errorText)" + + AuthorizationSequenceController.presentEmailComposeController(address: component.supportEmailAddress, subject: component.supportEmailSubject, body: presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: presentationData) + }) + ] + ) + controller.present(alertController, in: .window(.root)) } })) } else { @@ -379,6 +414,8 @@ public final class AuthorizationSequencePaymentScreen: ViewControllerComponentCo phoneNumber: String, phoneCodeHash: String, storeProduct: String, + supportEmailAddress: String, + supportEmailSubject: String, back: @escaping () -> Void ) { super.init(component: AuthorizationSequencePaymentScreenComponent( @@ -388,12 +425,14 @@ public final class AuthorizationSequencePaymentScreen: ViewControllerComponentCo presentationData: presentationData, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, - storeProduct: storeProduct + storeProduct: storeProduct, + supportEmailAddress: supportEmailAddress, + supportEmailSubject: supportEmailSubject ), navigationBarAppearance: .transparent, theme: .default, updatedPresentationData: (initial: presentationData, signal: .single(presentationData))) loadServerCountryCodes(accountManager: sharedContext.accountManager, engine: engine, completion: { [weak self] in if let strongSelf = self { - strongSelf.requestLayout(forceUpdate: true, transition: .immediate) + strongSelf.requestLayout(forceUpdate: true, transition: ContainedViewLayoutTransition.immediate) } }) diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 095e9a6bd8..de800066d2 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -587,7 +587,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch } } -public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool?, isClosed: Bool?, chatListController: ViewController?, joined: Bool, canSelect: Bool, customEdit: ((ContextController) -> Void)? = nil, customPinUnpin: ((ContextController) -> Void)? = nil, reorder: (() -> Void)? = nil) -> Signal<[ContextMenuItem], NoError> { +public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool?, isClosed: Bool?, chatListController: ViewController?, joined: Bool, canSelect: Bool, customEdit: ((ContextController) -> Void)? = nil, customPinUnpin: ((ContextController) -> Void)? = nil, reorder: (() -> Void)? = nil, onDeleted: (() -> Void)? = nil) -> Signal<[ContextMenuItem], NoError> { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) let strings = presentationData.strings @@ -914,32 +914,33 @@ public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, thr }))) } if channel.hasPermission(.deleteAllMessages) { - items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak chatListController] _, f in - f(.default) - - if let chatListController = chatListController as? ChatListControllerImpl { - chatListController.deletePeerThread(peerId: peerId, threadId: threadId) - } else if let chatListController { - let actionSheet = ActionSheetController(presentationData: presentationData) - var items: [ActionSheetItem] = [] - - items.append(ActionSheetTextItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true)) - items.append(ActionSheetButtonItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - let _ = context.engine.peers.removeForumChannelThread(id: peerId, threadId: threadId).startStandalone(completed: { - }) - })) - - actionSheet.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() + items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak chatListController] c, _ in + c?.dismiss(completion: { + if let chatListController = chatListController as? ChatListControllerImpl { + chatListController.deletePeerThread(peerId: peerId, threadId: threadId) + } else if let chatListController { + let actionSheet = ActionSheetController(presentationData: presentationData) + var items: [ActionSheetItem] = [] + + items.append(ActionSheetTextItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true)) + items.append(ActionSheetButtonItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + onDeleted?() + let _ = context.engine.peers.removeForumChannelThread(id: peerId, threadId: threadId).startStandalone(completed: { }) + })) + + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ]) ]) - ]) - chatListController.present(actionSheet, in: .window(.root)) - } + chatListController.present(actionSheet, in: .window(.root)) + } + }) }))) } } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 5b8e949137..8d8254d63d 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1027,144 +1027,175 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.chatListDisplayNode.mainContainerNode.peerSelected = { [weak self] peer, threadId, animated, activateInput, promoInfo in - guard let self else { - return - } - - var forumSourcePeer: Signal = .single(nil) - if case let .savedMessagesChats(peerId) = self.location, peerId != self.context.account.peerId { - forumSourcePeer = self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) - ) - } - - let _ = (combineLatest(queue: .mainQueue(), - self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) - |> take(1), - forumSourcePeer - ) - |> deliverOnMainQueue).start(next: { [weak self] combinedView, forumSourcePeer in - guard let self, let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else { - return - } - guard let navigationController = self.navigationController as? NavigationController else { + Task { @MainActor [weak self] in + guard let self else { return } - var peer = peer var threadId = threadId - if let forumSourcePeer { - threadId = peer.id.toInt64() - peer = forumSourcePeer + var subject: ChatControllerSubject? + if let threadIdValue = threadId, case let .user(user) = peer { + threadId = nil + + if case let .known(linkedForumId) = await self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id) + ).get(), let linkedForumId { + subject = .botForumThread(forumId: linkedForumId, threadId: threadIdValue) + } + } else if case let .user(user) = peer { + if case let .known(linkedForumId) = await self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id) + ).get(), let linkedForumId { + subject = .botForumThread(forumId: linkedForumId, threadId: EngineMessage.newTopicThreadId) + } + } + subject = nil + + var forumSourcePeer: Signal = .single(nil) + if case let .savedMessagesChats(peerId) = self.location, peerId != self.context.account.peerId { + forumSourcePeer = self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) } - var scrollToEndIfExists = false - if let layout = self.validLayout, case .regular = layout.metrics.widthClass { - scrollToEndIfExists = true - } - - var openAsInlineForum = true - - if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) { - openAsInlineForum = false - } else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) { - openAsInlineForum = false - } else { - if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { + let _ = (combineLatest(queue: .mainQueue(), + self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)]) + |> take(1), + forumSourcePeer + ) + |> deliverOnMainQueue).start(next: { [weak self] combinedView, forumSourcePeer in + guard let self, let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else { + return + } + guard let navigationController = self.navigationController as? NavigationController else { + return + } + + var peer = peer + var threadId = threadId + if let forumSourcePeer { + threadId = peer.id.toInt64() + peer = forumSourcePeer + } + + var scrollToEndIfExists = false + if let layout = self.validLayout, case .regular = layout.metrics.widthClass { + scrollToEndIfExists = true + } + + var openAsInlineForum = true + + if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) { + openAsInlineForum = false + } else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) { openAsInlineForum = false - } - } - - if openAsInlineForum, case let .channel(channel) = peer, channel.isForum, threadId == nil { - self.chatListDisplayNode.clearHighlightAnimated(true) - if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) { - self.setInlineChatList(location: nil) } else { - self.setInlineChatList(location: .forum(peerId: channel.id)) - } - return - } - - if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( - navigationController: navigationController, - context: self.context, - chatLocation: .replyThread(ChatReplyThreadMessage( - peerId: peer.id, - threadId: threadId, - channelMessageId: nil, - isChannelPost: false, - isForumPost: true, - isMonoforumPost: channel.isMonoForum, - maxMessage: nil, - maxReadIncomingMessageId: nil, - maxReadOutgoingMessageId: nil, - unreadCount: 0, - initialFilledHoles: IndexSet(), - initialAnchor: .automatic, - isNotAvailable: false - )), - subject: nil, - keepStack: .always - )) - - self.chatListDisplayNode.clearHighlightAnimated(true) - } else { - var navigationAnimationOptions: NavigationAnimationOptions = [] - var groupId: EngineChatList.Group = .root - if case let .chatList(groupIdValue) = self.location { - groupId = groupIdValue - if case .root = groupIdValue { - navigationAnimationOptions = .removeOnMasterDetails + if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages { + openAsInlineForum = false } } - let chatLocation: NavigateToChatControllerParams.Location - chatLocation = .peer(peer) - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, activateInput: (activateInput && !peer.isDeleted) ? .text : nil, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: navigationAnimationOptions, parentGroupId: groupId._asGroup(), chatListFilter: self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in - guard let self else { - return + if openAsInlineForum, case let .channel(channel) = peer, channel.isForum, threadId == nil { + self.chatListDisplayNode.clearHighlightAnimated(true) + if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) { + self.setInlineChatList(location: nil) + } else { + self.setInlineChatList(location: .forum(peerId: channel.id)) } - self.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) + return + } + + if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: .replyThread(ChatReplyThreadMessage( + peerId: peer.id, + threadId: threadId, + channelMessageId: nil, + isChannelPost: false, + isForumPost: true, + isMonoforumPost: channel.isMonoForum, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )), + subject: subject, + keepStack: .always + )) - if let promoInfo = promoInfo { - switch promoInfo { - case .proxy: - let _ = (ApplicationSpecificNotice.getProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager) - |> deliverOnMainQueue).startStandalone(next: { [weak self] value in - guard let self else { - return - } - if !value { - controller.displayPromoAnnouncement(text: self.presentationData.strings.DialogList_AdNoticeAlert) - let _ = ApplicationSpecificNotice.setProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager).startStandalone() - } - }) - case let .psa(type, _): - let _ = (ApplicationSpecificNotice.getPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id) - |> deliverOnMainQueue).startStandalone(next: { [weak self] value in - guard let self else { - return - } - if !value { - var text = self.presentationData.strings.ChatList_GenericPsaAlert - let key = "ChatList.PsaAlert.\(type)" - if let string = self.presentationData.strings.primaryComponent.dict[key] { - text = string - } else if let string = self.presentationData.strings.secondaryComponent?.dict[key] { - text = string - } - - controller.displayPromoAnnouncement(text: text) - let _ = ApplicationSpecificNotice.setPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).startStandalone() - } - }) + self.chatListDisplayNode.clearHighlightAnimated(true) + } else { + var navigationAnimationOptions: NavigationAnimationOptions = [] + var groupId: EngineChatList.Group = .root + if case let .chatList(groupIdValue) = self.location { + groupId = groupIdValue + if case .root = groupIdValue { + navigationAnimationOptions = .removeOnMasterDetails } } - })) - } - }) + + let chatLocation: NavigateToChatControllerParams.Location + chatLocation = .peer(peer) + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: chatLocation, + subject: subject, + activateInput: (activateInput && !peer.isDeleted) ? .text : nil, + scrollToEndIfExists: scrollToEndIfExists, + animated: !scrollToEndIfExists, + options: navigationAnimationOptions, + parentGroupId: groupId._asGroup(), + chatListFilter: self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in + guard let self else { + return + } + self.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) + + if let promoInfo = promoInfo { + switch promoInfo { + case .proxy: + let _ = (ApplicationSpecificNotice.getProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).startStandalone(next: { [weak self] value in + guard let self else { + return + } + if !value { + controller.displayPromoAnnouncement(text: self.presentationData.strings.DialogList_AdNoticeAlert) + let _ = ApplicationSpecificNotice.setProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager).startStandalone() + } + }) + case let .psa(type, _): + let _ = (ApplicationSpecificNotice.getPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id) + |> deliverOnMainQueue).startStandalone(next: { [weak self] value in + guard let self else { + return + } + if !value { + var text = self.presentationData.strings.ChatList_GenericPsaAlert + let key = "ChatList.PsaAlert.\(type)" + if let string = self.presentationData.strings.primaryComponent.dict[key] { + text = string + } else if let string = self.presentationData.strings.secondaryComponent?.dict[key] { + text = string + } + + controller.displayPromoAnnouncement(text: text) + let _ = ApplicationSpecificNotice.setPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).startStandalone() + } + }) + } + } + })) + } + }) + } } self.chatListDisplayNode.mainContainerNode.groupSelected = { [weak self] groupId in @@ -1177,9 +1208,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ApplicationSpecificNotice.displayChatListArchiveTooltip(accountManager: self.context.sharedContext.accountManager), self.context.engine.data.get( TelegramEngine.EngineData.Item.Configuration.GlobalPrivacy() - ) + ), + self.context.engine.messages.chatList(group: .archive, count: 20) |> take(1) ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] didDisplayTip, settings in + |> deliverOnMainQueue).startStandalone(next: { [weak self] didDisplayTip, settings, chatListHead in guard let self else { return } @@ -1192,7 +1224,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController navigationController.pushViewController(chatListController) } - if !didDisplayTip { + if !didDisplayTip, chatListHead.items.count < 10 { #if DEBUG #else let _ = ApplicationSpecificNotice.setDisplayChatListArchiveTooltip(accountManager: self.context.sharedContext.accountManager).startStandalone() @@ -4613,44 +4645,46 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let _ = (combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)) |> deliverOnMainQueue).startStandalone(next: { [weak self] _, chatListView in - guard let strongSelf = self else { - return - } - - /*if let scrollToTop = strongSelf.scrollToTop { - scrollToTop() - }*/ - - let tabsIsEmpty: Bool - if let (resolvedItems, displayTabsAtBottom, _) = strongSelf.tabContainerData { - tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom - } else { - tabsIsEmpty = true - } - let _ = tabsIsEmpty - //TODO:swap tabs - - let displaySearchFilters = true - - if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) { - let (filterContainerNode, activate) = filterContainerNodeAndActivate - if displaySearchFilters { - let searchTabsNode = SparseNode() - strongSelf.searchTabsNode = searchTabsNode - searchTabsNode.addSubnode(filterContainerNode) + Task { @MainActor in + guard let strongSelf = self else { + return } - activate(filter != .downloads) + /*if let scrollToTop = strongSelf.scrollToTop { + scrollToTop() + }*/ - if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { - searchContentNode.search(filter: filter, query: query) + let tabsIsEmpty: Bool + if let (resolvedItems, displayTabsAtBottom, _) = strongSelf.tabContainerData { + tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom + } else { + tabsIsEmpty = true } + let _ = tabsIsEmpty + //TODO:swap tabs + + let displaySearchFilters = true + + if let filterContainerNodeAndActivate = await strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) { + let (filterContainerNode, activate) = filterContainerNodeAndActivate + if displaySearchFilters { + let searchTabsNode = SparseNode() + strongSelf.searchTabsNode = searchTabsNode + searchTabsNode.addSubnode(filterContainerNode) + } + + activate(filter != .downloads) + + if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { + searchContentNode.search(filter: filter, query: query) + } + } + + let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) + strongSelf.setDisplayNavigationBar(false, transition: transition) + + (strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring)) } - - let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) - strongSelf.setDisplayNavigationBar(false, transition: transition) - - (strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring)) }) self.isSearchActive = true diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 357cf6e848..c413dc8a53 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -20,6 +20,7 @@ import ComponentFlow import ChatFolderLinkPreviewScreen import ChatListHeaderComponent import StoryPeerListComponent +import TelegramNotices public enum ChatListContainerNodeFilter: Equatable { case all @@ -1649,7 +1650,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } } - func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, (Bool) -> Void)? { + @MainActor + func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) async -> (ASDisplayNode, (Bool) -> Void)? { guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else { return nil } @@ -1688,6 +1690,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { self?.controller?.openAdInfo(node: node, adPeer: adPeer) } + let searchTips = await ApplicationSpecificNotice.getGlobalPostsSearch(accountManager: self.context.sharedContext.accountManager).get() + contentNode.displayGlobalPostsNewBadge = searchTips < 3 + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: contentNode, cancel: { [weak self] in if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index a1fc5e5fdc..289cfa96d8 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -131,6 +131,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var forumPeer: EnginePeer? private var hasPublicPostsTab = false private var showPublicPostsTab = false + public var displayGlobalPostsNewBadge = false private var shareStatusDisposable: MetaDisposable? @@ -715,7 +716,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: !isForum && self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter) } - self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: true, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition) + self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition) } } @@ -786,7 +787,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } let overflowInset: CGFloat = 20.0 - self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: true, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) if isFirstTime { self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 4611c2577b..86a748c929 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2061,7 +2061,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } var defaultFoundRemoteMessagesSignal: Signal<([FoundRemoteMessages], Bool), NoError> = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false)) - if key == .globalPosts { + if key == .globalPosts, let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_load_empty_global_posts"] as? Double, value != 0.0 { let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil), query: "", state: nil, limit: 50) |> map { resultData -> ChatListSearchMessagesResult in let (result, updatedState) = resultData @@ -3529,7 +3529,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } else { playlistLocation = .custom(messages: foundMessages |> map { message, a, b in return (message.map { $0._asMessage() }, a, b) - }, at: message.id, loadMore: { + }, canReorder: false, at: message.id, loadMore: { loadMore() }) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift index ac48fb5c95..7d52bddb6a 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift @@ -9,6 +9,7 @@ import AccountContext import ContextUI import AnimationCache import MultiAnimationRenderer +import TelegramNotices protocol ChatListSearchPaneNode: ASDisplayNode { var isReady: Signal { get } @@ -238,6 +239,11 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD } return } + + if key == .globalPosts { + let _ = ApplicationSpecificNotice.incrementGlobalPostsSearch(accountManager: self.context.sharedContext.accountManager).startStandalone() + } + #if DEBUG #else self.isAdjacentLoadingEnabled = true diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index ad74d8d1b4..01bdf22f8b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -580,6 +580,9 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.isForumOrMonoForum { threadId = message.threadId } + if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), let forumTopicData = peerData.forumTopicData { + threadId = forumTopicData.id + } self.interaction.messageSelected(peer, threadId, message, peerData.promoInfo) } else if let peer = peerData.peer.peer { self.interaction.peerSelected(peer, nil, nil, peerData.promoInfo, false) @@ -2509,6 +2512,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { forumThread = (threadInfo.id, threadInfo.info.title, threadInfo.info.icon, threadInfo.info.iconColor, nil, false) } } + if let forumTopicData, forumThread == nil, case let .user(user) = itemPeer.chatMainPeer, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) { + forumThread = (forumTopicData.id, forumTopicData.title, forumTopicData.iconFileId, forumTopicData.iconColor, forumTopicData.threadPeer, forumTopicData.isUnread) + } let messageText: String if let currentChatListText = currentChatListText, currentChatListText.0 == text { @@ -3421,7 +3427,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } else if forumThread != nil || !topForumTopicItems.isEmpty { if let forumThread = forumThread { - isFirstForumThreadSelectable = forumThread.isUnread + if case let .peer(peer) = item.content, case .user = peer.peer.chatMainPeer { + isFirstForumThreadSelectable = false + } else { + isFirstForumThreadSelectable = forumThread.isUnread + } + forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor)) } for topicItem in topForumTopicItems { @@ -3475,7 +3486,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { textMaxWidth -= 18.0 } - let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: (authorAttributedString == nil && itemTags.isEmpty) ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0))) + let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: (authorAttributedString == nil && itemTags.isEmpty && forumThread == nil) ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0))) let maxTitleLines: Int switch item.index { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 026dfa2641..cf42c530ac 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1876,17 +1876,17 @@ public final class ChatListNode: ListView { switch notice { case .xmasPremiumGift: let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.xmasPremiumGift.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in + self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in return true })) case .setupBirthday: let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in + self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in return true })) case .birthdayPremiumGift: let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.todayBirthdays.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in + self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in return true })) case .premiumGrace: @@ -2503,7 +2503,7 @@ public final class ChatListNode: ListView { case let .user(userType): if case let .user(user) = peer { match = true - if user.id.isVerificationCodes { + if user.id.isVerificationCodes || user.id.isTelegramNotifications { match = false } if let isBot = userType.isBot { diff --git a/submodules/ComponentFlow/Source/Components/Text.swift b/submodules/ComponentFlow/Source/Components/Text.swift index b364de1224..65d9e81587 100644 --- a/submodules/ComponentFlow/Source/Components/Text.swift +++ b/submodules/ComponentFlow/Source/Components/Text.swift @@ -30,12 +30,16 @@ public final class Text: Component { public final class View: UIView { private var measureState: MeasureState? - public func update(component: Text, availableSize: CGSize) -> CGSize { + public func update(component: Text, availableSize: CGSize, transition: ComponentTransition) -> CGSize { let attributedText = NSAttributedString(string: component.text, attributes: [ NSAttributedString.Key.font: component.font, NSAttributedString.Key.foregroundColor: component.color ]) + if let tintColor = component.tintColor { + transition.setTintColor(layer: self.layer, color: tintColor) + } + if let measureState = self.measureState { if measureState.attributedText.isEqual(to: attributedText) && measureState.availableSize == availableSize { return measureState.size @@ -71,11 +75,13 @@ public final class Text: Component { public let text: String public let font: UIFont public let color: UIColor + public let tintColor: UIColor? - public init(text: String, font: UIFont, color: UIColor) { + public init(text: String, font: UIFont, color: UIColor, tintColor: UIColor? = nil) { self.text = text self.font = font self.color = color + self.tintColor = tintColor } public static func ==(lhs: Text, rhs: Text) -> Bool { @@ -88,6 +94,9 @@ public final class Text: Component { if !lhs.color.isEqual(rhs.color) { return false } + if lhs.tintColor != rhs.tintColor { + return false + } return true } @@ -96,6 +105,6 @@ public final class Text: Component { } public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize) + return view.update(component: self, availableSize: availableSize, transition: transition) } } diff --git a/submodules/Components/BalancedTextComponent/Sources/BalancedTextComponent.swift b/submodules/Components/BalancedTextComponent/Sources/BalancedTextComponent.swift index 30453c2122..3908f9ae06 100644 --- a/submodules/Components/BalancedTextComponent/Sources/BalancedTextComponent.swift +++ b/submodules/Components/BalancedTextComponent/Sources/BalancedTextComponent.swift @@ -19,6 +19,7 @@ public final class BalancedTextComponent: Component { public let lineSpacing: CGFloat public let cutout: TextNodeCutout? public let insets: UIEdgeInsets + public let tintColor: UIColor? public let textShadowColor: UIColor? public let textShadowBlur: CGFloat? public let textStroke: (UIColor, CGFloat)? @@ -38,6 +39,7 @@ public final class BalancedTextComponent: Component { lineSpacing: CGFloat = 0.0, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), + tintColor: UIColor? = nil, textShadowColor: UIColor? = nil, textShadowBlur: CGFloat? = nil, textStroke: (UIColor, CGFloat)? = nil, @@ -56,6 +58,7 @@ public final class BalancedTextComponent: Component { self.lineSpacing = lineSpacing self.cutout = cutout self.insets = insets + self.tintColor = tintColor self.textShadowColor = textShadowColor self.textShadowBlur = textShadowBlur self.textStroke = textStroke @@ -94,7 +97,9 @@ public final class BalancedTextComponent: Component { if lhs.insets != rhs.insets { return false } - + if lhs.tintColor != rhs.tintColor { + return false + } if let lhsTextShadowColor = lhs.textShadowColor, let rhsTextShadowColor = rhs.textShadowColor { if !lhsTextShadowColor.isEqual(rhsTextShadowColor) { return false @@ -202,6 +207,10 @@ public final class BalancedTextComponent: Component { bestSize = (availableSize.width, bestInfo) } + if let tintColor = component.tintColor { + transition.setTintColor(layer: self.textView.layer, color: tintColor) + } + self.textView.frame = CGRect(origin: CGPoint(), size: bestSize.info.size) return bestSize.info.size } diff --git a/submodules/Components/MultilineTextComponent/Sources/MultilineTextComponent.swift b/submodules/Components/MultilineTextComponent/Sources/MultilineTextComponent.swift index cfcbaf9b85..11f08ffc52 100644 --- a/submodules/Components/MultilineTextComponent/Sources/MultilineTextComponent.swift +++ b/submodules/Components/MultilineTextComponent/Sources/MultilineTextComponent.swift @@ -18,6 +18,7 @@ public final class MultilineTextComponent: Component { public let lineSpacing: CGFloat public let cutout: TextNodeCutout? public let insets: UIEdgeInsets + public let tintColor: UIColor? public let textShadowColor: UIColor? public let textShadowBlur: CGFloat? public let textStroke: (UIColor, CGFloat)? @@ -36,6 +37,7 @@ public final class MultilineTextComponent: Component { lineSpacing: CGFloat = 0.0, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), + tintColor: UIColor? = nil, textShadowColor: UIColor? = nil, textShadowBlur: CGFloat? = nil, textStroke: (UIColor, CGFloat)? = nil, @@ -53,6 +55,7 @@ public final class MultilineTextComponent: Component { self.lineSpacing = lineSpacing self.cutout = cutout self.insets = insets + self.tintColor = tintColor self.textShadowColor = textShadowColor self.textShadowBlur = textShadowBlur self.textStroke = textStroke @@ -88,7 +91,9 @@ public final class MultilineTextComponent: Component { if lhs.insets != rhs.insets { return false } - + if lhs.tintColor != rhs.tintColor { + return false + } if let lhsTextShadowColor = lhs.textShadowColor, let rhsTextShadowColor = rhs.textShadowColor { if !lhsTextShadowColor.isEqual(rhsTextShadowColor) { return false @@ -169,6 +174,12 @@ public final class MultilineTextComponent: Component { let size = self.updateLayout(availableSize) + if let tintColor = component.tintColor { + transition.setTintColor(layer: self.layer, color: tintColor) + } else { + self.layer.layerTintColor = nil + } + return size } } diff --git a/submodules/Components/SheetComponent/Sources/SheetComponent.swift b/submodules/Components/SheetComponent/Sources/SheetComponent.swift index 28aa486e23..1d8c0f4788 100644 --- a/submodules/Components/SheetComponent/Sources/SheetComponent.swift +++ b/submodules/Components/SheetComponent/Sources/SheetComponent.swift @@ -64,6 +64,8 @@ public final class SheetComponent: C public let followContentSizeChanges: Bool public let clipsContent: Bool public let isScrollEnabled: Bool + public let hasDimView: Bool + public let autoAnimateOut: Bool public let externalState: ExternalState? public let animateOut: ActionSlot> public let onPan: () -> Void @@ -75,6 +77,8 @@ public final class SheetComponent: C followContentSizeChanges: Bool = false, clipsContent: Bool = false, isScrollEnabled: Bool = true, + hasDimView: Bool = true, + autoAnimateOut: Bool = true, externalState: ExternalState? = nil, animateOut: ActionSlot>, onPan: @escaping () -> Void = {}, @@ -85,6 +89,8 @@ public final class SheetComponent: C self.followContentSizeChanges = followContentSizeChanges self.clipsContent = clipsContent self.isScrollEnabled = isScrollEnabled + self.hasDimView = hasDimView + self.autoAnimateOut = autoAnimateOut self.externalState = externalState self.animateOut = animateOut self.onPan = onPan @@ -101,6 +107,15 @@ public final class SheetComponent: C if lhs.followContentSizeChanges != rhs.followContentSizeChanges { return false } + if lhs.isScrollEnabled != rhs.isScrollEnabled { + return false + } + if lhs.hasDimView != rhs.hasDimView { + return false + } + if lhs.autoAnimateOut != rhs.autoAnimateOut { + return false + } if lhs.animateOut != rhs.animateOut { return false } @@ -414,12 +429,22 @@ public final class SheetComponent: C self.currentAvailableSize = availableSize + if !component.hasDimView { + self.dimView.backgroundColor = .clear + } + if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) { self.animateIn() } else if !environment[SheetComponentEnvironment.self].value.isDisplaying, self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) { - self.animateOut(completion: {}) + if component.autoAnimateOut { + self.animateOut(completion: {}) + } + } + if !sheetEnvironment.isDisplaying && !component.autoAnimateOut { + + } else { + self.previousIsDisplaying = sheetEnvironment.isDisplaying } - self.previousIsDisplaying = sheetEnvironment.isDisplaying self.dismiss = sheetEnvironment.dismiss return availableSize diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 4eeac4b0c1..bd1bfaceac 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -134,9 +134,13 @@ open class ViewControllerComponentContainer: ViewController { } public final class AnimateInTransition { + public init() { + } } public final class AnimateOutTransition { + public init() { + } } public final class Node: ViewControllerTracingNode { @@ -356,7 +360,7 @@ open class ViewControllerComponentContainer: ViewController { } if let layout = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, transition: .immediate) + strongSelf.containerLayoutUpdated(layout, transition: ContainedViewLayoutTransition.immediate) } } }).strict() @@ -412,6 +416,14 @@ open class ViewControllerComponentContainer: ViewController { self.forceNextUpdate = false } + public func requestLayout(forceUpdate: Bool, transition: ComponentTransition) { + self.forceNextUpdate = forceUpdate + if self.isViewLoaded, let validLayout = self.validLayout { + self.containerLayoutUpdated(validLayout, transition: transition) + } + self.forceNextUpdate = false + } + override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) @@ -421,6 +433,15 @@ open class ViewControllerComponentContainer: ViewController { self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: ComponentTransition(transition)) } + public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ComponentTransition) { + super.containerLayoutUpdated(layout, transition: transition.containedViewLayoutTransition) + + let navigationHeight = self.navigationLayout(layout: layout).navigationFrame.maxY + + self.validLayout = layout + self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition) + } + public func updateComponent(component: AnyComponent, transition: ComponentTransition) { self.component = component self.node.updateComponent(component: component, transition: transition) diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 569cb412d7..67b130f2a0 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -201,8 +201,8 @@ private enum ContactListNodeEntry: Comparable, Identifiable { status = .none itemPeer = .deviceContact(stableId: id, contact: contact) } - if isSearch { - status = .none + if isSearch, case let .peer(peer, _) = itemPeer, let _ = peer?.addressName { + status = .addressName("") } var itemContextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? if isContextActionEnabled, let contextAction = interaction.contextAction { diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 06082cb5c4..a1344158eb 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -2213,6 +2213,7 @@ public protocol ContextExtractedContentSource: AnyObject { var keepDefaultContentTouches: Bool { get } var blurBackground: Bool { get } var shouldBeDismissed: Signal { get } + var additionalInsets: UIEdgeInsets { get } var actionsHorizontalAlignment: ContextActionsHorizontalAlignment { get } @@ -2237,6 +2238,10 @@ public extension ContextExtractedContentSource { return false } + var additionalInsets: UIEdgeInsets { + return .zero + } + var actionsHorizontalAlignment: ContextActionsHorizontalAlignment { return .default } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 3c1bfa50b9..e4bb7bc615 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -586,7 +586,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo let actionsEdgeInset: CGFloat let actionsSideInset: CGFloat let topInset: CGFloat = layout.insets(options: .statusBar).top + 8.0 - let bottomInset: CGFloat = 10.0 + var bottomInset: CGFloat = 10.0 let itemContentNode: ItemContentNode? let controllerContentNode: ControllerContentNode? @@ -903,6 +903,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo case let .extracted(source): keepInPlace = source.keepInPlace actionsHorizontalAlignment = source.actionsHorizontalAlignment + bottomInset += source.additionalInsets.bottom case .controller: //TODO: keepInPlace = false diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 99044f64e6..a800abaed0 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -194,6 +194,7 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel public private(set) final var visibleSize: CGSize = CGSize() public private(set) final var insets = UIEdgeInsets() public final var visualInsets: UIEdgeInsets? + private var itemOffsetInsets: UIEdgeInsets? public final var dynamicVisualInsets: (() -> UIEdgeInsets)? public private(set) final var headerInsets = UIEdgeInsets() public private(set) final var scrollIndicatorInsets = UIEdgeInsets() @@ -348,10 +349,13 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel public final var beganInteractiveDragging: (CGPoint) -> Void = { _ in } public final var endedInteractiveDragging: (CGPoint) -> Void = { _ in } public final var didEndScrolling: ((Bool) -> Void)? + public final var didEndScrollingWithOverscroll: (() -> Void)? + private var currentGeneralScrollDirection: GeneralScrollDirection? public final var generalScrollDirectionUpdated: (GeneralScrollDirection) -> Void = { _ in } + public var autoScrollWhenReordering = true public private(set) var isReordering = false public final var willBeginReorder: (CGPoint) -> Void = { _ in } public final var reorderBegan: () -> Void = { } @@ -668,6 +672,12 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel private func updateReordering(offset: CGFloat) { if let reorderNode = self.reorderNode { + if !self.autoScrollWhenReordering, case let .known(contentOffset) = self.visibleContentOffset() { + let updatedLocation = reorderNode.initialLocation.y + offset + if updatedLocation < self.insets.top - contentOffset { + return + } + } reorderNode.updateOffset(offset: offset) self.checkItemReordering() } @@ -883,6 +893,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel self.resetScrollIndicatorFlashTimer(start: false) self.isAuxiliaryDisplayLinkEnabled = true + + if scrollView.contentOffset.y < -48.0 { + self.didEndScrollingWithOverscroll?() + } } else { self.isDeceleratingAfterTracking = false self.resetHeaderItemsFlashTimer(start: true) @@ -1520,10 +1534,14 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel topItemFound = true } + if !topItemFound, self.stackFromBottom && !self.autoScrollWhenReordering, let itemNode = self.itemNodes.first, itemNode.apparentFrame.minY > 0.0 { + topItemFound = true + } + var topOffset: CGFloat if topItemFound { - let realTopItemEdge = itemNodes.first!.apparentFrame.origin.y + let realTopItemEdge = self.itemNodes.first!.apparentFrame.origin.y let realTopItemEdgeOffset = max(0.0, realTopItemEdge) topOffset = realTopItemEdgeOffset @@ -1848,7 +1866,7 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel nodes.append(.Placeholder(frame: node.apparentFrame)) } } - return ListViewState(insets: self.insets, visibleSize: self.visibleSize, invisibleInset: self.invisibleInset, nodes: nodes, scrollPosition: nil, stationaryOffset: nil, stackFromBottom: self.stackFromBottom) + return ListViewState(insets: self.insets, itemOffsetInsets: self.itemOffsetInsets ?? self.insets, visibleSize: self.visibleSize, invisibleInset: self.invisibleInset, nodes: nodes, scrollPosition: nil, stationaryOffset: nil, stackFromBottom: self.stackFromBottom) } public func addAfterTransactionsCompleted(_ f: @escaping () -> Void) { @@ -1886,6 +1904,7 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel self.insets = updateSizeAndInsets.insets self.headerInsets = updateSizeAndInsets.headerInsets ?? self.insets self.scrollIndicatorInsets = updateSizeAndInsets.scrollIndicatorInsets ?? self.insets + self.itemOffsetInsets = updateSizeAndInsets.itemOffsetInsets self.ensureTopInsetForOverlayHighlightedItems = updateSizeAndInsets.ensureTopInsetForOverlayHighlightedItems let wasIgnoringScrollingEvents = self.ignoreScrollingEvents @@ -2474,14 +2493,14 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel takenAnimation = true if abs(layout.size.height - previousApparentHeight) > CGFloat.ulpOfOne { - node.addApparentHeightAnimation(layout.size.height, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, invertOffsetDirection: invertOffsetDirection, update: { [weak node] progress, currentValue in + node.addApparentHeightAnimation(layout.size.height, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp, invertOffsetDirection: invertOffsetDirection, update: { [weak node] progress, currentValue in if let node = node { node.animateFrameTransition(progress, currentValue) } }) if node.rotated { node.transitionOffset += previousApparentHeight - layout.size.height - node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addTransitionOffsetAnimation(0.0, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) } } } @@ -2523,16 +2542,16 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel } else { node.transitionOffset += transitionOffsetDelta } - node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addTransitionOffsetAnimation(0.0, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) if previousInsets != layout.insets { node.insets = previousInsets - node.addInsetsAnimationToValue(layout.insets, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addInsetsAnimationToValue(layout.insets, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) } } } else { if !nodeFrame.size.height.isEqual(to: node.apparentHeight) { let addAnimation = previousFrame?.height != nodeFrame.size.height - node.addApparentHeightAnimation(nodeFrame.size.height, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, invertOffsetDirection: invertOffsetDirection, update: { [weak node] progress, currentValue in + node.addApparentHeightAnimation(nodeFrame.size.height, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp, invertOffsetDirection: invertOffsetDirection, update: { [weak node] progress, currentValue in if let node = node, addAnimation { node.animateFrameTransition(progress, currentValue) } @@ -2550,10 +2569,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel } else { node.transitionOffset += transitionOffsetDelta } - node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addTransitionOffsetAnimation(0.0, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) if previousInsets != layout.insets { node.insets = previousInsets - node.addInsetsAnimationToValue(layout.insets, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addInsetsAnimationToValue(layout.insets, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) } } else { if self.debugInfo { @@ -2562,21 +2581,21 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel if !node.rotated { if !node.insets.top.isZero { node.transitionOffset += node.insets.top - node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addTransitionOffsetAnimation(0.0, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), beginAt: timestamp) } } - node.animateInsertion(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), options: ListViewItemAnimationOptions(short: invertOffsetDirection)) + node.animateInsertion(timestamp, duration: (node.updateAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), options: ListViewItemAnimationOptions(short: invertOffsetDirection)) } } } else if animateAlpha { if previousFrame == nil { if forceAnimateInsertion { - node.animateInsertion(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), options: ListViewItemAnimationOptions(short: true)) + node.animateInsertion(timestamp, duration: (node.insertionAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor(), options: ListViewItemAnimationOptions(short: true)) } else if animateFullTransition { node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) node.layer.animateScale(from: 0.7, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) } else { - node.animateAdded(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor()) + node.animateAdded(timestamp, duration: (node.insertionAnimationDuration() ?? insertionAnimationDuration) * UIView.animationDurationFactor()) } } } @@ -3186,6 +3205,7 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel self.insets = updateSizeAndInsets.insets self.headerInsets = updateSizeAndInsets.headerInsets ?? self.insets self.scrollIndicatorInsets = updateSizeAndInsets.scrollIndicatorInsets ?? self.insets + self.itemOffsetInsets = updateSizeAndInsets.itemOffsetInsets self.ensureTopInsetForOverlayHighlightedItems = updateSizeAndInsets.ensureTopInsetForOverlayHighlightedItems self.visibleSize = updateSizeAndInsets.size @@ -4662,7 +4682,7 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel var offsetRanges = OffsetRanges() var scrollingForReorder = false - if let reorderOffset = self.reorderNode?.currentOffset(), !self.itemNodes.isEmpty { + if self.autoScrollWhenReordering, let reorderOffset = self.reorderNode?.currentOffset(), !self.itemNodes.isEmpty { let effectiveInsets = self.visualInsets ?? self.insets var offset: CGFloat = 6.0 diff --git a/submodules/Display/Source/ListViewIntermediateState.swift b/submodules/Display/Source/ListViewIntermediateState.swift index e80b3c1185..2edc464c3e 100644 --- a/submodules/Display/Source/ListViewIntermediateState.swift +++ b/submodules/Display/Source/ListViewIntermediateState.swift @@ -138,16 +138,18 @@ public struct ListViewUpdateSizeAndInsets { public var insets: UIEdgeInsets public var headerInsets: UIEdgeInsets? public var scrollIndicatorInsets: UIEdgeInsets? + public var itemOffsetInsets: UIEdgeInsets? public var duration: Double public var curve: ListViewAnimationCurve public var ensureTopInsetForOverlayHighlightedItems: CGFloat? public var customAnimationTransition: ControlledTransition? - public init(size: CGSize, insets: UIEdgeInsets, headerInsets: UIEdgeInsets? = nil, scrollIndicatorInsets: UIEdgeInsets? = nil, duration: Double, curve: ListViewAnimationCurve, ensureTopInsetForOverlayHighlightedItems: CGFloat? = nil, customAnimationTransition: ControlledTransition? = nil) { + public init(size: CGSize, insets: UIEdgeInsets, headerInsets: UIEdgeInsets? = nil, scrollIndicatorInsets: UIEdgeInsets? = nil, itemOffsetInsets: UIEdgeInsets? = nil, duration: Double, curve: ListViewAnimationCurve, ensureTopInsetForOverlayHighlightedItems: CGFloat? = nil, customAnimationTransition: ControlledTransition? = nil) { self.size = size self.insets = insets self.headerInsets = headerInsets self.scrollIndicatorInsets = scrollIndicatorInsets + self.itemOffsetInsets = itemOffsetInsets self.duration = duration self.curve = curve self.ensureTopInsetForOverlayHighlightedItems = ensureTopInsetForOverlayHighlightedItems @@ -306,6 +308,7 @@ struct ListViewInsertionPoint { struct ListViewState { var insets: UIEdgeInsets + var itemOffsetInsets: UIEdgeInsets var visibleSize: CGSize let invisibleInset: CGFloat var nodes: [ListViewStateNode] @@ -524,7 +527,7 @@ struct ListViewState { if fixedNode == nil { for i in 0 ..< self.nodes.count { let node = self.nodes[i] - if let index = node.index , node.frame.maxY >= self.insets.top { + if let index = node.index , node.frame.maxY >= self.itemOffsetInsets.top { fixedNode = (i, index, node.frame) break } @@ -549,7 +552,7 @@ struct ListViewState { if index != currentUpperNode.index - 1 { if currentUpperNode.frame.minY > -self.invisibleInset - CGFloat.ulpOfOne { var directionHint: ListViewInsertionOffsetDirection? - if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat.ulpOfOne { + if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.itemOffsetInsets.top - CGFloat.ulpOfOne { directionHint = ListViewInsertionOffsetDirection(hint) } return ListViewInsertionPoint(index: currentUpperNode.index - 1, point: CGPoint(x: 0.0, y: currentUpperNode.frame.minY), direction: directionHint ?? .Up) @@ -564,10 +567,10 @@ struct ListViewState { if currentUpperNode.index != 0 && currentUpperNode.frame.minY > -self.invisibleInset - CGFloat.ulpOfOne { var directionHint: ListViewInsertionOffsetDirection? if let hint = insertDirectionHints[currentUpperNode.index - 1] { - if currentUpperNode.frame.minY >= self.insets.top - CGFloat.ulpOfOne { + if currentUpperNode.frame.minY >= self.itemOffsetInsets.top - CGFloat.ulpOfOne { directionHint = ListViewInsertionOffsetDirection(hint) } - } else if currentUpperNode.frame.minY >= self.insets.top - CGFloat.ulpOfOne && !fixedNodeIsStationary { + } else if currentUpperNode.frame.minY >= self.itemOffsetInsets.top - CGFloat.ulpOfOne && !fixedNodeIsStationary { directionHint = .Down } @@ -582,7 +585,7 @@ struct ListViewState { if index != currentLowerNode.index + 1 { if currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat.ulpOfOne { var directionHint: ListViewInsertionOffsetDirection? - if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne { + if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.itemOffsetInsets.bottom + CGFloat.ulpOfOne { directionHint = ListViewInsertionOffsetDirection(hint) } return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down) @@ -597,7 +600,7 @@ struct ListViewState { if currentLowerNode.index != itemCount - 1 && currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat.ulpOfOne { var directionHint: ListViewInsertionOffsetDirection? - if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne { + if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.itemOffsetInsets.bottom + CGFloat.ulpOfOne { directionHint = ListViewInsertionOffsetDirection(hint) } return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down) @@ -810,7 +813,7 @@ struct ListViewState { if let direction = direction { offsetDirection = ListViewInsertionOffsetDirection(direction) } else { - if nodeFrame.maxY < self.insets.top + CGFloat.ulpOfOne { + if nodeFrame.maxY < self.itemOffsetInsets.top + CGFloat.ulpOfOne { offsetDirection = .Down } else { offsetDirection = .Up @@ -818,12 +821,12 @@ struct ListViewState { } operations.append(.Remove(index: index, offsetDirection: offsetDirection)) - if let referenceNode = referenceNode , animated { + if let referenceNode = referenceNode, animated { self.nodes.insert(.Placeholder(frame: nodeFrame), at: index) operations.append(.InsertDisappearingPlaceholder(index: index, referenceNode: referenceNode, offsetDirection: offsetDirection.inverted())) } else { - if nodeFrame.maxY > self.insets.top - CGFloat.ulpOfOne { - if let direction = direction , direction == .Down && node.frame.minY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne { + if nodeFrame.maxY > self.itemOffsetInsets.top - CGFloat.ulpOfOne { + if let direction = direction , direction == .Down && node.frame.minY < self.visibleSize.height - self.itemOffsetInsets.bottom + CGFloat.ulpOfOne { for i in (0 ..< index).reversed() { var frame = self.nodes[i].frame frame.origin.y += nodeFrame.size.height @@ -859,7 +862,7 @@ struct ListViewState { if let direction = direction { offsetDirection = ListViewInsertionOffsetDirection(direction) } else { - if node.frame.maxY < self.insets.top + CGFloat.ulpOfOne { + if node.frame.maxY < self.itemOffsetInsets.top + CGFloat.ulpOfOne { offsetDirection = .Down } else { offsetDirection = .Up diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift index 7c3ff8eee7..42c6a99572 100644 --- a/submodules/Display/Source/ListViewItemNode.swift +++ b/submodules/Display/Source/ListViewItemNode.swift @@ -587,6 +587,14 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { self.setAnimationForKey("transitionOffset", animation: animation) } + open func insertionAnimationDuration() -> Double? { + return nil + } + + open func updateAnimationDuration() -> Double? { + return nil + } + open func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) { } diff --git a/submodules/Display/Source/ListViewReorderingItemNode.swift b/submodules/Display/Source/ListViewReorderingItemNode.swift index b1dd58b192..4cbd14fdd1 100644 --- a/submodules/Display/Source/ListViewReorderingItemNode.swift +++ b/submodules/Display/Source/ListViewReorderingItemNode.swift @@ -51,7 +51,7 @@ final class ListViewReorderingItemNode: ASDisplayNode { var currentState: (Int, Int)? private let copyView: CopyView - private let initialLocation: CGPoint + let initialLocation: CGPoint init(itemNode: ListViewItemNode, initialLocation: CGPoint, hasShadow: Bool) { self.itemNode = itemNode diff --git a/submodules/Display/Source/TextAlertController.swift b/submodules/Display/Source/TextAlertController.swift index 291446f04b..2224e1a4d0 100644 --- a/submodules/Display/Source/TextAlertController.swift +++ b/submodules/Display/Source/TextAlertController.swift @@ -463,8 +463,8 @@ public final class TextAlertContentNode: AlertContentNode { } } -public func textAlertController(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, dismissOnOutsideTap: Bool = true) -> AlertController { - return AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title, text: text, actions: actions, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap)) +public func textAlertController(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { + return AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title, text: text, actions: actions, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction)) } public func standardTextAlertController(theme: AlertControllerTheme, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { diff --git a/submodules/FileMediaResourceStatus/Sources/FileMediaResourceStatus.swift b/submodules/FileMediaResourceStatus/Sources/FileMediaResourceStatus.swift index 5c7fb53fd9..a9e00836eb 100644 --- a/submodules/FileMediaResourceStatus/Sources/FileMediaResourceStatus.swift +++ b/submodules/FileMediaResourceStatus/Sources/FileMediaResourceStatus.swift @@ -5,12 +5,12 @@ import SwiftSignalKit import UniversalMediaPlayer import AccountContext -private func internalMessageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal { +private func internalMessageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool, isSavedMusic: Bool) -> Signal { guard let playerType = peerMessageMediaPlayerType(message) else { return .single(nil) } - if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) { + if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList, isSavedMusic: isSavedMusic) { return context.sharedContext.mediaManager.filteredPlaylistState(accountId: context.account.id, playlistId: playlistId, itemId: itemId, type: playerType) |> mapToSignal { state -> Signal in return .single(state?.status) @@ -20,32 +20,32 @@ private func internalMessageFileMediaPlaybackStatus(context: AccountContext, fil } } -public func messageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal { +public func messageFileMediaPlaybackStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool, isSavedMusic: Bool) -> Signal { var duration = 0.0 if let value = file.duration { duration = Double(value) } let defaultStatus = MediaPlayerStatus(generationTimestamp: 0.0, duration: duration, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused, soundEnabled: true) - return internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) + return internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList, isSavedMusic: isSavedMusic) |> map { status in return status ?? defaultStatus } } -public func messageFileMediaPlaybackAudioLevelEvents(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool) -> Signal { +public func messageFileMediaPlaybackAudioLevelEvents(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isGlobalSearch: Bool, isDownloadList: Bool, isSavedMusic: Bool) -> Signal { guard let playerType = peerMessageMediaPlayerType(message) else { return .never() } - if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) { + if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList, isSavedMusic: isSavedMusic) { return context.sharedContext.mediaManager.filteredPlayerAudioLevelEvents(accountId: context.account.id, playlistId: playlistId, itemId: itemId, type: playerType) } else { return .never() } } -public func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isSharedMedia: Bool = false, isGlobalSearch: Bool = false, isDownloadList: Bool = false) -> Signal { - let playbackStatus = internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList) |> map { status -> MediaPlayerPlaybackStatus? in +public func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMediaFile, message: EngineMessage, isRecentActions: Bool, isSharedMedia: Bool = false, isGlobalSearch: Bool = false, isDownloadList: Bool = false, isSavedMusic: Bool = false) -> Signal { + let playbackStatus = internalMessageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: isGlobalSearch, isDownloadList: isDownloadList, isSavedMusic: isSavedMusic) |> map { status -> MediaPlayerPlaybackStatus? in return status?.status } diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift index baffc6678d..1a7d6f3e0a 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift @@ -172,7 +172,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { containerSize: CGSize(width: size.width, height: 44.0) ) let tabSelectorFrameOriginX = floorToScreenPixels((size.width - tabSelectorSize.width) / 2.0) - let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: size.height - tabSelectorSize.height - 9.0), size: tabSelectorSize) + let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: size.height - tabSelectorSize.height - 10.0), size: tabSelectorSize) if let tabSelectorView = self.tabSelector.view { if tabSelectorView.superview == nil { self.view.addSubview(tabSelectorView) diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index fe3759663f..be9aee83dc 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -684,7 +684,7 @@ private final class PendingInAppPurchaseState: Codable { case gift(peerId: EnginePeer.Id) case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?, text: String?, entities: [MessageTextEntity]?) case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32) - case stars(count: Int64) + case stars(count: Int64, peerId: EnginePeer.Id?) case starsGift(peerId: EnginePeer.Id, count: Int64) case starsGiveaway(stars: Int64, boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, users: Int32) case authCode(restore: Bool, phoneNumber: String, phoneCodeHash: String) @@ -724,7 +724,8 @@ private final class PendingInAppPurchaseState: Codable { ) case .stars: self = .stars( - count: try container.decode(Int64.self, forKey: .stars) + count: try container.decode(Int64.self, forKey: .stars), + peerId: try container.decodeIfPresent(Int64.self, forKey: .peer).flatMap { EnginePeer.Id($0) } ) case .starsGift: self = .starsGift( @@ -784,9 +785,10 @@ private final class PendingInAppPurchaseState: Codable { try container.encodeIfPresent(prizeDescription, forKey: .prizeDescription) try container.encode(randomId, forKey: .randomId) try container.encode(untilDate, forKey: .untilDate) - case let .stars(count): + case let .stars(count, peerId): try container.encode(PurposeType.stars.rawValue, forKey: .type) try container.encode(count, forKey: .stars) + try container.encodeIfPresent(peerId?.toInt64(), forKey: .peer) case let .starsGift(peerId, count): try container.encode(PurposeType.starsGift.rawValue, forKey: .type) try container.encode(peerId.toInt64(), forKey: .peer) @@ -825,8 +827,8 @@ private final class PendingInAppPurchaseState: Codable { self = .giftCode(peerIds: peerIds, boostPeer: boostPeer, text: text, entities: entities) case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, _, _): self = .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate) - case let .stars(count, _, _): - self = .stars(count: count) + case let .stars(count, _, _, peerId): + self = .stars(count: count, peerId: peerId) case let .starsGift(peerId, count, _, _): self = .starsGift(peerId: peerId, count: count) case let .starsGiveaway(stars, boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, _, _, users): @@ -851,8 +853,8 @@ private final class PendingInAppPurchaseState: Codable { return .giftCode(peerIds: peerIds, boostPeer: boostPeer, currency: currency, amount: amount, text: text, entities: entities) case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate): return .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount) - case let .stars(count): - return .stars(count: count, currency: currency, amount: amount) + case let .stars(count, peerId): + return .stars(count: count, currency: currency, amount: amount, peerId: peerId) case let .starsGift(peerId, count): return .starsGift(peerId: peerId, count: count, currency: currency, amount: amount) case let .starsGiveaway(stars, boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, users): diff --git a/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift b/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift index 639acc53b9..0e2526e212 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift @@ -105,7 +105,7 @@ final class ItemListControllerTabsContentNode: NavigationBarContentNode { environment: {}, containerSize: CGSize(width: size.width, height: 44.0) ) - let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: floor((size.height - tabSelectorSize.height) / 2.0) + 4.0), size: tabSelectorSize) + let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: floor((size.height - tabSelectorSize.height) / 2.0) + 3.0), size: tabSelectorSize) if let tabSelectorView = self.tabSelector.view { if tabSelectorView.superview == nil { self.view.addSubview(tabSelectorView) diff --git a/submodules/LegacyComponents/Sources/ActionStage.mm b/submodules/LegacyComponents/Sources/ActionStage.mm index 0d55828276..3904249b9b 100644 --- a/submodules/LegacyComponents/Sources/ActionStage.mm +++ b/submodules/LegacyComponents/Sources/ActionStage.mm @@ -246,7 +246,7 @@ ActionStage *ActionStageInstance() int length = (int)path.length; #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif unichar newPath[path.length]; diff --git a/submodules/LegacyComponents/Sources/Freedom.mm b/submodules/LegacyComponents/Sources/Freedom.mm index 38436e31e9..f0828b4ce4 100644 --- a/submodules/LegacyComponents/Sources/Freedom.mm +++ b/submodules/LegacyComponents/Sources/Freedom.mm @@ -258,7 +258,7 @@ static bool consumeField(const char *desc, int length, int &offset, uint32_t &ou outName = (uint32_t)murMurHashBytes32((void *)(desc + offset), i - offset); #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif char buf[i - offset + 1]; diff --git a/submodules/LegacyComponents/Sources/POPDecayAnimationInternal.h b/submodules/LegacyComponents/Sources/POPDecayAnimationInternal.h index fa822bb748..d138d3f448 100644 --- a/submodules/LegacyComponents/Sources/POPDecayAnimationInternal.h +++ b/submodules/LegacyComponents/Sources/POPDecayAnimationInternal.h @@ -27,7 +27,7 @@ static void decay_position(CGFloat *x, CGFloat *v, NSUInteger count, CFTimeInter // x0 = x; // x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 - deceleration) #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif float v0[count]; diff --git a/submodules/LegacyComponents/Sources/TGStringUtils.mm b/submodules/LegacyComponents/Sources/TGStringUtils.mm index 52208738fb..6c903dd263 100644 --- a/submodules/LegacyComponents/Sources/TGStringUtils.mm +++ b/submodules/LegacyComponents/Sources/TGStringUtils.mm @@ -1140,7 +1140,7 @@ int32_t phoneMatchHash(NSString *phone) { int length = (int)phone.length; #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif char cleanString[length]; diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 7b4fe7a8b6..0de2906f25 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -384,6 +384,8 @@ public final class ListMessageFileItemNode: ListMessageNode { private var contentSizeValue: CGSize? private var currentLeftOffset: CGFloat = 0.0 + var reorderControlNode: ItemListEditableReorderControlNode? + private var currentIsRestricted = false private var cachedSearchResult: CachedChatListSearchResult? @@ -568,6 +570,13 @@ public final class ListMessageFileItemNode: ListMessageNode { } } + override public func isReorderable(at point: CGPoint) -> Bool { + if let reorderControlNode = self.reorderControlNode, reorderControlNode.frame.contains(point) { + return true + } + return false + } + override public func asyncLayout() -> (_ item: ListMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: Bool, _ mergedBottom: Bool, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let titleNodeMakeLayout = self.titleNode.asyncLayout() let textNodeMakeLayout = TextNode.asyncLayout(self.textNode) @@ -575,6 +584,7 @@ public final class ListMessageFileItemNode: ListMessageNode { let extensionIconTextMakeLayout = TextNode.asyncLayout(self.extensionIconText) let dateNodeMakeLayout = TextNode.asyncLayout(self.dateNode) let iconImageLayout = self.iconImageNode.asyncLayout() + let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode) let currentMedia = self.currentMedia let currentMessage = self.message @@ -618,6 +628,7 @@ public final class ListMessageFileItemNode: ListMessageNode { var updatedStatusSignal: Signal? var updatedPlaybackStatusSignal: Signal? var updatedFetchControls: FetchControls? + var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? var isAudio = false var isVoice = false @@ -676,8 +687,8 @@ public final class ListMessageFileItemNode: ListMessageNode { } if descriptionString.isEmpty { descriptionString = authorString.first ?? "" - } else { - descriptionString = "\(descriptionString) • \(authorString.first ?? "")" + } else if let authorStringFirst = authorString.first { + descriptionString = "\(descriptionString) • \(authorStringFirst)" } } @@ -896,7 +907,7 @@ public final class ListMessageFileItemNode: ListMessageNode { if statusUpdated && item.displayFileInfo { if let file = selectedMedia as? TelegramMediaFile { - updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, message: EngineMessage(message), isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList) + updatedStatusSignal = messageFileMediaResourceStatus(context: item.context, file: file, message: EngineMessage(message), isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList, isSavedMusic: item.isSavedMusic) |> mapToSignal { value -> Signal in if case .Fetching = value.fetchStatus, !item.isDownloadList { return .single(value) |> delay(0.1, queue: Queue.concurrentDefaultQueue()) @@ -923,7 +934,7 @@ public final class ListMessageFileItemNode: ListMessageNode { } } if isVoice { - updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, message: EngineMessage(message), isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList) + updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: item.context, file: file, message: EngineMessage(message), isRecentActions: false, isGlobalSearch: item.isGlobalSearchResult, isDownloadList: item.isDownloadList, isSavedMusic: false) } } else if let image = selectedMedia as? TelegramMediaImage { updatedStatusSignal = messageImageMediaResourceStatus(context: item.context, image: image, message: EngineMessage(message), isRecentActions: false, isSharedMedia: true, isGlobalSearch: item.isGlobalSearchResult || item.isDownloadList) @@ -978,17 +989,25 @@ public final class ListMessageFileItemNode: ListMessageNode { captionText = text } + var reorderInset: CGFloat = 0.0 + + if item.canReorder { + let sizeAndApply = reorderControlLayout(item.presentationData.theme.theme) + reorderControlSizeAndApply = sizeAndApply + reorderInset = sizeAndApply.0 + } + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message?.timestamp ?? 0, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat) let dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor) let (dateNodeLayout, dateNodeApply) = dateNodeMakeLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 12.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(item.context, params.width - leftInset - leftOffset - rightInset - dateNodeLayout.size.width - 4.0, item.presentationData.theme.theme, titleText, titleExtraData) + let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(item.context, params.width - leftInset - leftOffset - rightInset - dateNodeLayout.size.width - 4.0 - reorderInset, item.presentationData.theme.theme, titleText, titleExtraData) - let (textNodeLayout, textNodeApply) = textNodeMakeLayout(TextNodeLayoutArguments(attributedString: captionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 30.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (textNodeLayout, textNodeApply) = textNodeMakeLayout(TextNodeLayoutArguments(attributedString: captionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 30.0 - reorderInset, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(item.context, params.width - leftInset - rightInset - 30.0, item.presentationData.theme.theme, descriptionText, descriptionExtraData) + let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(item.context, params.width - leftInset - rightInset - 30.0 - reorderInset, item.presentationData.theme.theme, descriptionText, descriptionExtraData) var (extensionTextLayout, extensionTextApply) = extensionIconTextMakeLayout(TextNodeLayoutArguments(attributedString: extensionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 38.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) if extensionTextLayout.truncated, let text = extensionText?.string { @@ -1094,7 +1113,7 @@ public final class ListMessageFileItemNode: ListMessageNode { strongSelf.currentLeftOffset = leftOffset if let _ = updatedTheme { - if item.displayBackground { + if item.displayBackground || item.canReorder { let backgroundNode: ASDisplayNode if let current = strongSelf.backgroundNode { backgroundNode = current @@ -1103,7 +1122,7 @@ public final class ListMessageFileItemNode: ListMessageNode { strongSelf.backgroundNode = backgroundNode strongSelf.insertSubnode(backgroundNode, at: 0) } - backgroundNode.backgroundColor = item.presentationData.theme.theme.list.itemBlocksBackgroundColor + backgroundNode.backgroundColor = item.canReorder ? item.presentationData.theme.theme.list.plainBackgroundColor : item.presentationData.theme.theme.list.itemBlocksBackgroundColor } strongSelf.separatorNode.backgroundColor = item.presentationData.theme.theme.list.itemPlainSeparatorColor @@ -1135,6 +1154,24 @@ public final class ListMessageFileItemNode: ListMessageNode { }) } + if let reorderControlSizeAndApply = reorderControlSizeAndApply { + let reorderControlFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - reorderControlSizeAndApply.0, y: 0.0), size: CGSize(width: reorderControlSizeAndApply.0, height: nodeLayout.contentSize.height)) + if strongSelf.reorderControlNode == nil { + let reorderControlNode = reorderControlSizeAndApply.1(nodeLayout.contentSize.height, false, .immediate) + strongSelf.reorderControlNode = reorderControlNode + strongSelf.addSubnode(reorderControlNode) + reorderControlNode.frame = reorderControlFrame + } else if let reorderControlNode = strongSelf.reorderControlNode { + let _ = reorderControlSizeAndApply.1(nodeLayout.contentSize.height, false, .immediate) + transition.updateFrame(node: reorderControlNode, frame: reorderControlFrame) + } + } else if let reorderControlNode = strongSelf.reorderControlNode { + strongSelf.reorderControlNode = nil + transition.updateAlpha(node: reorderControlNode, alpha: 0.0, completion: { [weak reorderControlNode] _ in + reorderControlNode?.removeFromSupernode() + }) + } + transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel))) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - UIScreenPixel), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel - nodeLayout.insets.bottom)) diff --git a/submodules/ListMessageItem/Sources/ListMessageItem.swift b/submodules/ListMessageItem/Sources/ListMessageItem.swift index f48e0ee89b..5968f972ed 100644 --- a/submodules/ListMessageItem/Sources/ListMessageItem.swift +++ b/submodules/ListMessageItem/Sources/ListMessageItem.swift @@ -55,15 +55,17 @@ public final class ListMessageItem: ListViewItem { let hintIsLink: Bool let isGlobalSearchResult: Bool let isDownloadList: Bool + let isSavedMusic: Bool let displayFileInfo: Bool let displayBackground: Bool + let canReorder: Bool let style: ItemListStyle let header: ListViewItemHeader? public let selectable: Bool = true - public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message?, translateToLanguage: String? = nil, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false, isDownloadList: Bool = false, displayFileInfo: Bool = true, displayBackground: Bool = false, style: ItemListStyle = .plain) { + public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, interaction: ListMessageItemInteraction, message: Message?, translateToLanguage: String? = nil, selection: ChatHistoryMessageSelection, displayHeader: Bool, customHeader: ListViewItemHeader? = nil, hintIsLink: Bool = false, isGlobalSearchResult: Bool = false, isDownloadList: Bool = false, isSavedMusic: Bool = false, displayFileInfo: Bool = true, displayBackground: Bool = false, canReorder: Bool = false, style: ItemListStyle = .plain) { self.presentationData = presentationData self.context = context self.chatLocation = chatLocation @@ -81,8 +83,10 @@ public final class ListMessageItem: ListViewItem { self.hintIsLink = hintIsLink self.isGlobalSearchResult = isGlobalSearchResult self.isDownloadList = isDownloadList + self.isSavedMusic = isSavedMusic self.displayFileInfo = displayFileInfo self.displayBackground = displayBackground + self.canReorder = canReorder self.style = style } diff --git a/submodules/MediaPlayer/Sources/MediaPlayerTimeTextNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerTimeTextNode.swift index 1e379d80e4..824b909963 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerTimeTextNode.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerTimeTextNode.swift @@ -193,7 +193,7 @@ public final class MediaPlayerTimeTextNode: ASDisplayNode { self.state = MediaPlayerTimeTextNodeState(hours: timestamp / (60 * 60), minutes: timestamp % (60 * 60) / 60, seconds: timestamp % 60) } else { let timestampSeconds: Double - if !statusValue.generationTimestamp.isZero { + if !statusValue.generationTimestamp.isZero && isPlaying { timestampSeconds = timestamp + (CACurrentMediaTime() - statusValue.generationTimestamp) } else { timestampSeconds = timestamp diff --git a/submodules/MtProtoKit/Sources/MTGzip.m b/submodules/MtProtoKit/Sources/MTGzip.m index a131b25d61..53e0f8af1f 100644 --- a/submodules/MtProtoKit/Sources/MTGzip.m +++ b/submodules/MtProtoKit/Sources/MTGzip.m @@ -11,7 +11,7 @@ int windowBits = 15 + 32; //Default + gzip header instead of zlib header int retCode; #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #pragma clang diagnostic ignored "-Wgnu-folding-constant" diff --git a/submodules/MtProtoKit/Sources/MTInternalMessageParser.m b/submodules/MtProtoKit/Sources/MTInternalMessageParser.m index 9137814a02..99ba8646cf 100644 --- a/submodules/MtProtoKit/Sources/MTInternalMessageParser.m +++ b/submodules/MtProtoKit/Sources/MTInternalMessageParser.m @@ -689,7 +689,7 @@ int windowBits = 15 + 32; //Default + gzip header instead of zlib header int retCode; #pragma clang diagnostic push -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 260000 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500 #pragma clang diagnostic ignored "-Wvla-cxx-extension" #endif #pragma clang diagnostic ignored "-Wgnu-folding-constant" diff --git a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift index 9949f1a771..755c5137b8 100644 --- a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift +++ b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift @@ -16,7 +16,7 @@ public struct ExternalMusicAlbumArtResourceId { } public var uniqueId: String { - return "ext-album-art-\(isThumbnail ? "thump" : "full")-\(self.title.replacingOccurrences(of: "/", with: "_"))-\(self.performer.replacingOccurrences(of: "/", with: "_"))" + return "ext-album-art-\(self.isThumbnail ? "thumb" : "full")-\(self.title.replacingOccurrences(of: "/", with: "_"))-\(self.performer.replacingOccurrences(of: "/", with: "_"))" } public var hashValue: Int { diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index cbb6249951..0d2a373bb0 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -3252,7 +3252,7 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen |> map { thumbnailData in return Tuple(thumbnailData, nil, false) } - } else if let albumArt = albumArt { + } else if let albumArt = albumArt, !albumArt.thumbnailResource.title.isEmpty && !albumArt.thumbnailResource.performer.isEmpty { if thumbnail { immediateArtworkData = albumArtThumbnailData(engine: engine, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously) |> map { thumbnailData in @@ -3261,6 +3261,8 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen } else { immediateArtworkData = albumArtFullSizeDatas(engine: engine, file: fileReference, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource) } + } else { + immediateArtworkData = .single(Tuple(nil, nil, false)) } return combineLatest(fileArtworkData, immediateArtworkData) diff --git a/submodules/Postbox/Sources/CachedPeerDataView.swift b/submodules/Postbox/Sources/CachedPeerDataView.swift index 9e432d5f25..d1787f8033 100644 --- a/submodules/Postbox/Sources/CachedPeerDataView.swift +++ b/submodules/Postbox/Sources/CachedPeerDataView.swift @@ -2,19 +2,74 @@ import Foundation final class MutableCachedPeerDataView: MutablePostboxView { let peerId: PeerId + let trackAssociatedMessages: Bool var cachedPeerData: CachedPeerData? + var associatedMessages: [MessageId: Message] = [:] - init(postbox: PostboxImpl, peerId: PeerId) { + init(postbox: PostboxImpl, peerId: PeerId, trackAssociatedMessages: Bool) { self.peerId = peerId + self.trackAssociatedMessages = trackAssociatedMessages self.cachedPeerData = postbox.cachedPeerDataTable.get(peerId) + if let cachedPeerData = self.cachedPeerData { + for id in cachedPeerData.messageIds { + if let message = postbox.getMessage(id) { + self.associatedMessages[message.id] = message + } + } + } } func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { - if let cachedPeerData = transaction.currentUpdatedCachedPeerData[self.peerId] { + if let cachedPeerData = transaction.currentUpdatedCachedPeerData[self.peerId]?.updated { self.cachedPeerData = cachedPeerData + if self.trackAssociatedMessages { + self.associatedMessages.removeAll() + for id in cachedPeerData.messageIds { + if let message = postbox.getMessage(id) { + self.associatedMessages[message.id] = message + } + } + } + return true } else { - return false + var updatedIds = Set() + if self.trackAssociatedMessages { + if let cachedPeerData = self.cachedPeerData { + for peerId in Set(cachedPeerData.messageIds.map(\.peerId)) { + if let operations = transaction.currentOperationsByPeerId[peerId] { + for operation in operations { + switch operation { + case let .InsertMessage(message): + if cachedPeerData.messageIds.contains(message.id) { + updatedIds.insert(message.id) + } + case let .Remove(indices): + for index in indices { + if cachedPeerData.messageIds.contains(index.0.id) { + updatedIds.insert(index.0.id) + } + } + default: + break + } + } + } + } + } + } + if !updatedIds.isEmpty { + for id in updatedIds { + if let message = postbox.getMessage(id) { + self.associatedMessages[message.id] = message + } else { + self.associatedMessages.removeValue(forKey: id) + } + } + return true + } else { + return false + } } } @@ -30,9 +85,11 @@ final class MutableCachedPeerDataView: MutablePostboxView { public final class CachedPeerDataView: PostboxView { public let peerId: PeerId public let cachedPeerData: CachedPeerData? + public let associatedMessages: [MessageId: Message] init(_ view: MutableCachedPeerDataView) { self.peerId = view.peerId self.cachedPeerData = view.cachedPeerData + self.associatedMessages = view.associatedMessages } } diff --git a/submodules/Postbox/Sources/ChatListTable.swift b/submodules/Postbox/Sources/ChatListTable.swift index 7fcc985468..a857fadfc7 100644 --- a/submodules/Postbox/Sources/ChatListTable.swift +++ b/submodules/Postbox/Sources/ChatListTable.swift @@ -373,7 +373,7 @@ final class ChatListTable: Table { return result } - func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set, updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { + func replay(postbox: PostboxImpl, historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set, updatedPeerCachedData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)], updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { var changedPeerIds = Set() for peerId in historyOperationsByPeerId.keys { changedPeerIds.insert(peerId) @@ -384,6 +384,23 @@ final class ChatListTable: Table { for peerId in updatedChatListInclusions.keys { changedPeerIds.insert(peerId) } + for (peerId, update) in updatedPeerCachedData { + if let previous = update.previous { + if self.seedConfiguration.decodeAssociatedChatListPeerId(previous) != self.seedConfiguration.decodeAssociatedChatListPeerId(update.updated) { + changedPeerIds.insert(peerId) + } + } else { + changedPeerIds.insert(peerId) + } + } + var additionalChangedPeerIds = Set() + for peerId in changedPeerIds { + //TODO:release move this to seed configuration + if let peer = postbox.peerTable.get(peerId), peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 { + additionalChangedPeerIds.insert(associatedPeerId) + } + } + changedPeerIds.formUnion(additionalChangedPeerIds) self.ensureInitialized(groupId: .root) @@ -396,8 +413,8 @@ final class ChatListTable: Table { let topMessage = messageHistoryTable.topIndex(peerId: peerId) let embeddedChatStateOverrideTimestamp = peerChatInterfaceStateTable.get(peerId)?.overrideChatTimestamp - let rawTopMessageIndex: MessageIndex? - let topMessageIndex: MessageIndex? + var rawTopMessageIndex: MessageIndex? + var topMessageIndex: MessageIndex? if let topMessage = topMessage { var updatedTimestamp = topMessage.timestamp rawTopMessageIndex = MessageIndex(id: topMessage.id, timestamp: topMessage.timestamp) @@ -413,6 +430,18 @@ final class ChatListTable: Table { rawTopMessageIndex = nil } + if let cachedData = postbox.cachedPeerDataTable.get(peerId), let associatedChatListPeerId = self.seedConfiguration.decodeAssociatedChatListPeerId(cachedData) { + if let associatedTopMessage = messageHistoryTable.topIndex(peerId: associatedChatListPeerId) { + if let currentTopMessageIndex = topMessageIndex { + if currentTopMessageIndex.timestamp < associatedTopMessage.timestamp { + topMessageIndex = MessageIndex(id: currentTopMessageIndex.id, timestamp: associatedTopMessage.timestamp) + } + } else { + topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: associatedTopMessage.timestamp) + } + } + } + var updatedIndex = self.indexTable.setTopMessageIndex(peerId: peerId, index: topMessageIndex) if let updatedInclusion = updatedChatListInclusions[peerId] { updatedIndex = self.indexTable.setInclusion(peerId: peerId, inclusion: updatedInclusion) diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 2a3d47aca1..b06eed24f4 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -1040,19 +1040,71 @@ public final class ChatListView { self.groupId = mutableView.groupId var entries: [ChatListEntry] = [] + + //TODO:release + var linkedEntries: [PeerId: [MutableChatListEntry.MessageEntryData]] = [:] + if "".isEmpty { + for entry in mutableView.sampledState.entries { + guard case let .MessageEntry(entryData) = entry else { + continue + } + if let peer = entryData.renderedPeer.peer, peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 { + if linkedEntries[associatedPeerId] == nil { + linkedEntries[associatedPeerId] = [] + } + linkedEntries[associatedPeerId]?.append(entryData) + } + } + } + for entry in mutableView.sampledState.entries { switch entry { case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer, peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 { + continue + } + + //TODO:release + var index = entryData.index + var messages = entryData.messages + var readState = entryData.readState + var forumTopicData = entryData.displayAsRegularChat ? nil : entryData.forumTopicData + if let linkedEntries = linkedEntries[entryData.index.messageIndex.id.peerId] { + for entry in linkedEntries { + if entry.index.messageIndex.timestamp >= index.messageIndex.timestamp { + index = ChatListIndex(pinningIndex: index.pinningIndex, messageIndex: MessageIndex(id: index.messageIndex.id, timestamp: entry.index.messageIndex.timestamp)) + messages = entry.messages + forumTopicData = entry.forumTopicData + } + if let entryReadState = entry.readState { + if var readStateValue = readState { + var states = readStateValue.state.states + for (namespace, state) in entryReadState.state.states { + if let index = states.firstIndex(where: { $0.0 == namespace }) { + states[index] = (namespace, states[index].1.withAddedCount(state.count)) + } else { + states.append((namespace, state)) + } + } + readStateValue.state = .init(states: states) + readState = readStateValue + } else { + readState = entryReadState + } + } + } + } + entries.append(.MessageEntry(ChatListEntry.MessageEntryData( - index: entryData.index, - messages: entryData.messages, - readState: entryData.readState, + index: index, + messages: messages, + readState: readState, isRemovedFromTotalUnreadCount: entryData.isRemovedFromTotalUnreadCount, embeddedInterfaceState: entryData.embeddedInterfaceState, renderedPeer: entryData.renderedPeer, presence: entryData.presence, summaryInfo: entryData.tagSummaryInfo, - forumTopicData: entryData.displayAsRegularChat ? nil : entryData.forumTopicData, + forumTopicData: forumTopicData, topForumTopics: entryData.displayAsRegularChat ? [] : entryData.topForumTopics, hasFailed: entryData.hasFailedMessages, isContact: entryData.isContact, @@ -1061,6 +1113,7 @@ public final class ChatListView { extractedCachedData: entryData.extractedCachedData ))) case let .HoleEntry(hole): + entries.removeAll() entries.append(.HoleEntry(hole)) case .IntermediateMessageEntry: assertionFailure() diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index d875bfee09..90a3909e89 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -651,6 +651,8 @@ public struct MessageGroupInfo: Equatable { } public final class Message { + public static let newTopicThreadId: Int64 = Int64(Int32.max - 1) + public final class AssociatedThreadInfo: Equatable { public let title: String public let icon: Int64? @@ -905,6 +907,7 @@ public struct MessageThreadKey: Hashable { public final class StoreMessage { public let id: StoreMessageId + public let customStableId: UInt32? public let timestamp: Int32 public let globallyUniqueId: Int64? public let groupingKey: Int64? @@ -919,8 +922,9 @@ public final class StoreMessage { public let attributes: [MessageAttribute] public let media: [Media] - public init(id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + public init(id: MessageId, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = .Id(id) + self.customStableId = customStableId self.globallyUniqueId = globallyUniqueId self.groupingKey = groupingKey self.threadId = threadId @@ -936,8 +940,9 @@ public final class StoreMessage { self.media = media } - public init(peerId: PeerId, namespace: MessageId.Namespace, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + public init(peerId: PeerId, namespace: MessageId.Namespace, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = .Partial(peerId, namespace) + self.customStableId = customStableId self.timestamp = timestamp self.globallyUniqueId = globallyUniqueId self.groupingKey = groupingKey @@ -953,8 +958,9 @@ public final class StoreMessage { self.media = media } - public init(id: StoreMessageId, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + public init(id: StoreMessageId, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = id + self.customStableId = customStableId self.timestamp = timestamp self.globallyUniqueId = globallyUniqueId self.groupingKey = groupingKey @@ -978,33 +984,38 @@ public final class StoreMessage { } } + public func withUpdatedCustomStableId(_ customStableId: UInt32?) -> StoreMessage { + return StoreMessage(id: self.id, customStableId: customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, 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, attributes: attributes, media: self.media) + } + public func withUpdatedFlags(_ flags: StoreMessageFlags) -> StoreMessage { if flags == self.flags { return self } else { - return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media) + return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media) } } public func withUpdatedMedia(_ media: [Media]) -> StoreMessage { - return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, 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, attributes: self.attributes, media: media) + return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, 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, attributes: self.attributes, media: media) } public func withUpdatedAttributes(_ attributes: [MessageAttribute]) -> StoreMessage { - return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, 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, attributes: attributes, media: self.media) + return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, 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, attributes: attributes, media: self.media) } public func withUpdatedLocalTags(_ localTags: LocalMessageTags) -> StoreMessage { if localTags == self.localTags { return self } else { - return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: self.media) + return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: self.media) } } } final class InternalStoreMessage { let id: MessageId + let customStableId: UInt32? let timestamp: Int32 let globallyUniqueId: Int64? let groupingKey: Int64? @@ -1024,8 +1035,9 @@ 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, customTags: [MemoryBuffer], forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + init(id: MessageId, customStableId: UInt32?, 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.customStableId = customStableId self.timestamp = timestamp self.globallyUniqueId = globallyUniqueId self.groupingKey = groupingKey diff --git a/submodules/Postbox/Sources/MessageCustomTagHoleIndexTable.swift b/submodules/Postbox/Sources/MessageCustomTagHoleIndexTable.swift index 2b47af1327..76f6e05890 100644 --- a/submodules/Postbox/Sources/MessageCustomTagHoleIndexTable.swift +++ b/submodules/Postbox/Sources/MessageCustomTagHoleIndexTable.swift @@ -75,7 +75,7 @@ final class MessageCustomTagHoleIndexTable: Table { if !self.metadataTable.isPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag) { self.metadataTable.setPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag) - if let namespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] { + if let namespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) { for namespace in namespaces { var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:] self.addInternal(peerId: peerId, threadId: threadId, tag: tag, tagValue: tagValue, namespace: namespace, range: 1 ... (Int32.max - 1), operations: &operations) diff --git a/submodules/Postbox/Sources/MessageCustomTagWithTagHoleIndexTable.swift b/submodules/Postbox/Sources/MessageCustomTagWithTagHoleIndexTable.swift index 1c48954f1d..aa04433d6b 100644 --- a/submodules/Postbox/Sources/MessageCustomTagWithTagHoleIndexTable.swift +++ b/submodules/Postbox/Sources/MessageCustomTagWithTagHoleIndexTable.swift @@ -79,7 +79,7 @@ final class MessageCustomTagWithTagHoleIndexTable: Table { if !self.metadataTable.isPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag, regularTag: regularTag) { self.metadataTable.setPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag, regularTag: regularTag) - if let namespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] { + if let namespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) { for namespace in namespaces { var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:] self.addInternal(peerId: peerId, threadId: threadId, tag: tag, tagValue: tagValue, regularTag: regularTag, namespace: namespace, range: 1 ... (Int32.max - 1), operations: &operations) diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 7c097a125d..2d290a7545 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -431,10 +431,10 @@ final class MessageHistoryTable: Table { 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, customTags: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + internalStoreMessages.append(InternalStoreMessage(id: id, customStableId: message.customStableId, 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: self.seedConfiguration.customTagsFromAttributes(message.attributes), 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: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + internalStoreMessages.append(InternalStoreMessage(id: id, customStableId: message.customStableId, 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: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) } } return internalStoreMessages @@ -1084,7 +1084,7 @@ final class MessageHistoryTable: Table { var type: Int8 = 0 sharedBuffer.write(&type, offset: 0, length: 1) - var stableId: UInt32 = self.historyMetadataTable.getNextStableMessageIndexId() + var stableId: UInt32 = message.customStableId ?? self.historyMetadataTable.getNextStableMessageIndexId() sharedBuffer.write(&stableId, offset: 0, length: 4) var stableVersion: UInt32 = 0 @@ -2115,7 +2115,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, 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) + let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, customStableId: nil, 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 diff --git a/submodules/Postbox/Sources/MessageHistoryThreadHoleIndexTable.swift b/submodules/Postbox/Sources/MessageHistoryThreadHoleIndexTable.swift index b10bd75ae9..8c2dc30af8 100644 --- a/submodules/Postbox/Sources/MessageHistoryThreadHoleIndexTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryThreadHoleIndexTable.swift @@ -113,7 +113,7 @@ final class MessageHistoryThreadHoleIndexTable: Table { postboxLog("MessageHistoryThreadHoleIndexTable: Initializing \(peerId) \(threadId)") self.metadataTable.setIsThreadHoleIndexInitialized(peerId: peerId, threadId: threadId) - if let messageNamespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] { + if let messageNamespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) { for namespace in messageNamespaces { var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:] self.add(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1), operations: &operations) diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index b35d8366c3..a8b000f97d 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -332,6 +332,7 @@ final class MutableMessageHistoryView: MutablePostboxView { private var userId: Int64? fileprivate var peerStoryStats: [PeerId: PeerStoryStats] = [:] + fileprivate var typingDraft: Message? init( postbox: PostboxImpl, @@ -410,6 +411,8 @@ final class MutableMessageHistoryView: MutablePostboxView { self.render(postbox: postbox) let _ = self.updateStoryStats(postbox: postbox) + self.reloadTypingDraft(postbox: postbox) + let _ = self.updateThreadInfos(postbox: postbox, updatedIds: nil) } @@ -436,6 +439,8 @@ final class MutableMessageHistoryView: MutablePostboxView { self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) let _ = self.updateStoryStats(postbox: postbox) + + self.reloadTypingDraft(postbox: postbox) } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { @@ -447,14 +452,14 @@ final class MutableMessageHistoryView: MutablePostboxView { switch self.peerIds { case let .single(peerId, threadId): if threadId == nil { - if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { + if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated { if updatedData.associatedHistoryMessageId != nil { self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId) } } } case let .associated(peerId, associatedId): - if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { + if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated { if updatedData.associatedHistoryMessageId != associatedId { self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId) } @@ -839,7 +844,7 @@ final class MutableMessageHistoryView: MutablePostboxView { switch additionalDatas[i] { case let .cachedPeerData(peerId, currentData): currentCachedPeerData = currentData - if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { + if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated { if currentData?.messageIds != updatedData.messageIds { updatedCachedPeerDataMessages = true } @@ -1039,9 +1044,86 @@ final class MutableMessageHistoryView: MutablePostboxView { } } + switch self.peerIds { + case let .single(peerId, threadId): + let location = PeerAndThreadId(peerId: peerId, threadId: threadId) + if let typingDraftUpdate = transaction.updatedTypingDrafts[location] { + if let typingDraft = typingDraftUpdate.value { + self.typingDraft = self.renderTypingDraft(postbox: postbox, typingDraft: typingDraft) + } else { + self.typingDraft = nil + } + hasChanges = true + } + case .external: + break + case .associated: + break + } + return hasChanges } + private func reloadTypingDraft(postbox: PostboxImpl) { + guard case let .single(peerId, threadId) = self.peerIds else { + self.typingDraft = nil + return + } + if let typingDraft = postbox.currentTypingDrafts[PeerAndThreadId(peerId: peerId, threadId: threadId)] { + self.typingDraft = self.renderTypingDraft(postbox: postbox, typingDraft: typingDraft) + } else { + self.typingDraft = nil + } + } + + private func renderTypingDraft(postbox: PostboxImpl, typingDraft: PostboxImpl.TypingDraft) -> Message? { + guard case let .single(peerId, _) = self.peerIds else { + return nil + } + guard let peer = postbox.peerTable.get(peerId), let author = postbox.peerTable.get(typingDraft.authorId) else { + return nil + } + + var peers = SimpleDictionary() + peers[peer.id] = peer + peers[author.id] = author + + var associatedThreadInfo: Message.AssociatedThreadInfo? + if let threadId = typingDraft.threadId, let data = postbox.messageHistoryThreadIndexTable.get(peerId: peerId, threadId: threadId) { + associatedThreadInfo = postbox.seedConfiguration.decodeMessageThreadInfo(data.data) + } + + return Message( + stableId: typingDraft.stableId, + stableVersion: typingDraft.stableVersion, + id: MessageId( + peerId: peerId, + namespace: 1, + id: Int32.max - 50000), + globallyUniqueId: nil, + groupingKey: nil, + groupInfo: nil, + threadId: typingDraft.threadId, + timestamp: typingDraft.timestamp, + flags: [.Incoming], + tags: [], + globalTags: [], + localTags: [], + customTags: [], + forwardInfo: nil, + author: author, + text: typingDraft.text, + attributes: typingDraft.attributes, + media: [], + peers: peers, + associatedMessages: SimpleDictionary(), + associatedMessageIds: [], + associatedMedia: [:], + associatedThreadInfo: associatedThreadInfo, + associatedStories: [:] + ) + } + private func updateThreadInfos(postbox: PostboxImpl, updatedIds: Set?) -> Bool { if self.mapReadStatesFromThreads, case let .single(peerId, peerThreadId) = self.peerIds, peerThreadId == nil { switch self.sampledState { @@ -1446,6 +1528,16 @@ public final class MessageHistoryView: PostboxView { } } + if !self.holeLater, let typingDraft = mutableView.typingDraft { + entries.append(MessageHistoryEntry( + message: typingDraft, + isRead: false, + location: nil, + monthLocation: nil, + attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false) + )) + } + self.entries = entries self.peerStoryStats = mutableView.peerStoryStats } diff --git a/submodules/Postbox/Sources/PeerView.swift b/submodules/Postbox/Sources/PeerView.swift index 42e37958ad..f7ff4c114e 100644 --- a/submodules/Postbox/Sources/PeerView.swift +++ b/submodules/Postbox/Sources/PeerView.swift @@ -149,7 +149,7 @@ final class MutablePeerView: MutablePostboxView { var peersUpdated = false var updateMessages = false - if let cachedData = updatedCachedPeerData[self.contactPeerId], self.cachedData == nil || !self.cachedData!.isEqual(to: cachedData) { + if let cachedData = updatedCachedPeerData[self.contactPeerId]?.updated, self.cachedData == nil || !self.cachedData!.isEqual(to: cachedData) { if self.cachedData?.messageIds != cachedData.messageIds { updateMessages = true } @@ -286,7 +286,7 @@ final class MutablePeerView: MutablePostboxView { } if let associatedPeerId = peer.associatedPeerId { - if let value = updatedCachedPeerData[associatedPeerId] { + if let value = updatedCachedPeerData[associatedPeerId]?.updated { if let current = self.associatedCachedData[associatedPeerId] { if !current.isEqual(to: value) { self.associatedCachedData[associatedPeerId] = value diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 1a7854d0bf..98264a8cd0 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1393,6 +1393,27 @@ public final class Transaction { assert(!self.disposed) self.postbox!.reindexSavedMessagesCustomTagsWithTagsIfNeeded(peerId: peerId, threadId: threadId, tag: tag) } + + public func getCurrentTypingDraft(location: PeerAndThreadId) -> (id: Int64, stableId: UInt32, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])? { + assert(!self.disposed) + if let value = self.postbox!.currentTypingDrafts[location] { + return ( + value.id, + value.stableId, + value.authorId, + value.timestamp, + value.text, + value.attributes + ) + } else { + return nil + } + } + + public func combineTypingDrafts(locations: Set, update: (PeerAndThreadId, (id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) -> (id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) { + assert(!self.disposed) + self.postbox!.combineTypingDrafts(locations: locations, update: update) + } } public enum PostboxResult { @@ -1594,7 +1615,7 @@ final class PostboxImpl { private var currentUpdatedPeers: [PeerId: Peer] = [:] private var currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)] = [:] private var currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] = [:] - private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:] + private var currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)] = [:] private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:] private var currentUpdatedPeerChatListEmbeddedStates = Set() private var currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:] @@ -1635,6 +1656,76 @@ final class PostboxImpl { private var currentStoryTopItemEvents: [StoryTopItemsTable.Event] = [] private var currentStoryEvents: [StoryTable.Event] = [] + struct TypingDraft: Equatable { + var id: Int64 + var stableId: UInt32 + var stableVersion: UInt32 + var threadId: Int64? + var authorId: PeerId + var timestamp: Int32 + var text: String + var attributes: [MessageAttribute] + var addedAtTimestamp: Double + + init(id: Int64, stableId: UInt32, stableVersion: UInt32, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute], addedAtTimestamp: Double) { + self.id = id + self.stableId = stableId + self.stableVersion = stableVersion + self.threadId = threadId + self.authorId = authorId + self.timestamp = timestamp + self.text = text + self.attributes = attributes + self.addedAtTimestamp = addedAtTimestamp + } + + static func ==(lhs: TypingDraft, rhs: TypingDraft) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.stableId != rhs.stableId { + return false + } + if lhs.stableVersion != rhs.stableVersion { + return false + } + if lhs.threadId != rhs.threadId { + return false + } + if lhs.authorId != rhs.authorId { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.attributes.count != rhs.attributes.count { + return false + } + for i in 0 ..< lhs.attributes.count { + let lhsEncoder = PostboxEncoder() + lhs.attributes[i].encode(lhsEncoder) + let rhsEncoder = PostboxEncoder() + rhs.attributes[i].encode(rhsEncoder) + if lhsEncoder.makeData() != rhsEncoder.makeData() { + return false + } + } + return true + } + } + + struct TypingDraftUpdate { + var value: TypingDraft? + } + + fileprivate(set) var currentTypingDrafts: [PeerAndThreadId: TypingDraft] = [:] + private var currentUpdatedTypingDrafts: [PeerAndThreadId: TypingDraftUpdate] = [:] + private var nextTypingDraftExpirationTimestamp: Double? + private var nextTypingDraftExpirationTimer: SwiftSignalKit.Timer? + var hiddenChatIds: Set { if self.currentHiddenChatIds.isEmpty { return Set() @@ -2008,7 +2099,7 @@ final class PostboxImpl { if let forwardInfo = message.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) } - return .update(StoreMessage(id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: storeForwardInfo, authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: storeForwardInfo, authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media)) } else { return .skip } @@ -2019,6 +2110,7 @@ final class PostboxImpl { } deinit { + self.nextTypingDraftExpirationTimer?.invalidate() if let tempDir = self.tempDir { TempBox.shared.dispose(tempDir) } @@ -2401,6 +2493,89 @@ final class PostboxImpl { } } + fileprivate func combineTypingDrafts(locations: Set, update: (PeerAndThreadId, (id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) -> (id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) { + for location in locations { + var updated: (id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])? + let current = self.currentTypingDrafts[location] + if let current { + updated = update(location, (current.id, current.threadId, current.authorId, current.timestamp, current.text, current.attributes)) + } else { + updated = update(location, nil) + } + if let updated { + let stableId: UInt32 + let stableVersion: UInt32 + if let current, current.id == updated.id { + stableId = current.stableId + stableVersion = 100000 + current.stableVersion + 1 + } else { + stableId = self.messageHistoryMetadataTable.getNextStableMessageIndexId() + stableVersion = 100000 + } + let mappedDraft = TypingDraft(id: updated.id, stableId: stableId, stableVersion: stableVersion, threadId: updated.threadId, authorId: updated.authorId, timestamp: updated.timestamp, text: updated.text, attributes: updated.attributes, addedAtTimestamp: CFAbsoluteTimeGetCurrent()) + if self.currentTypingDrafts[location] != mappedDraft { + self.currentTypingDrafts[location] = mappedDraft + self.currentUpdatedTypingDrafts[location] = TypingDraftUpdate(value: mappedDraft) + } + } else if self.currentTypingDrafts[location] != nil { + self.currentTypingDrafts.removeValue(forKey: location) + self.currentUpdatedTypingDrafts[location] = TypingDraftUpdate(value: nil) + } + } + } + + private func restartTypingDraftExpirationTimerIfNeeded() { + let expirationTimeout: Double = 20.0 + + var nextTypingDraftExpirationTimestamp: Double? + for (_, draft) in self.currentTypingDrafts { + if let nextTypingDraftExpirationTimestampValue = nextTypingDraftExpirationTimestamp { + nextTypingDraftExpirationTimestamp = min(draft.addedAtTimestamp + expirationTimeout, nextTypingDraftExpirationTimestampValue) + } else { + nextTypingDraftExpirationTimestamp = draft.addedAtTimestamp + expirationTimeout + } + } + + if let nextTypingDraftExpirationTimestamp { + if self.nextTypingDraftExpirationTimer == nil || nextTypingDraftExpirationTimestamp != self.nextTypingDraftExpirationTimestamp { + let timeout = nextTypingDraftExpirationTimestamp - CFAbsoluteTimeGetCurrent() + + self.nextTypingDraftExpirationTimer?.invalidate() + self.nextTypingDraftExpirationTimer = SwiftSignalKit.Timer(timeout: max(0.0, timeout - 0.1), repeat: false, completion: { [weak self] in + guard let self else { + return + } + let _ = self.transaction { _ in + self.processTypingDraftExpirations(expirationTimeout: expirationTimeout) + }.startStandalone() + }, queue: self.queue) + self.nextTypingDraftExpirationTimer?.start() + } + } else { + self.nextTypingDraftExpirationTimestamp = nil + if let nextTypingDraftExpirationTimer = self.nextTypingDraftExpirationTimer { + self.nextTypingDraftExpirationTimer = nil + nextTypingDraftExpirationTimer.invalidate() + } + } + } + + private func processTypingDraftExpirations(expirationTimeout: Double) { + let timestamp = CFAbsoluteTimeGetCurrent() + var removedKeys: [PeerAndThreadId] = [] + for (key, draft) in self.currentTypingDrafts { + if draft.addedAtTimestamp + expirationTimeout >= timestamp { + removedKeys.append(key) + } + } + if !removedKeys.isEmpty { + for key in removedKeys { + self.currentTypingDrafts.removeValue(forKey: key) + self.currentUpdatedTypingDrafts[key] = TypingDraftUpdate(value: nil) + } + } + } + func renderIntermediateMessage(_ message: IntermediateMessage) -> Message { let renderedMessage = self.messageHistoryTable.renderMessage(message, peerTable: self.peerTable, threadIndexTable: self.messageHistoryThreadIndexTable, storyTable: self.storyTable) @@ -2427,7 +2602,7 @@ final class PostboxImpl { } private func beforeCommit(currentTransaction: Transaction) -> (updatedTransactionStateVersion: Int64?, updatedMasterClientId: Int64?) { - self.chatListTable.replay(historyOperationsByPeerId: self.currentOperationsByPeerId, updatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, updatedChatListInclusions: self.currentUpdatedChatListInclusions, messageHistoryTable: self.messageHistoryTable, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable, operations: &self.currentChatListOperations) + self.chatListTable.replay(postbox: self, historyOperationsByPeerId: self.currentOperationsByPeerId, updatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, updatedPeerCachedData: self.currentUpdatedCachedPeerData, updatedChatListInclusions: self.currentUpdatedChatListInclusions, messageHistoryTable: self.messageHistoryTable, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable, operations: &self.currentChatListOperations) self.peerChatTopTaggedMessageIdsTable.replay(historyOperationsByPeerId: self.currentOperationsByPeerId) @@ -2534,7 +2709,8 @@ final class PostboxImpl { storySubscriptionsEvents: self.currentStorySubscriptionsEvents, storyItemsEvents: self.currentStoryItemsEvents, currentStoryTopItemEvents: self.currentStoryTopItemEvents, - storyEvents: self.currentStoryEvents + storyEvents: self.currentStoryEvents, + updatedTypingDrafts: self.currentUpdatedTypingDrafts ) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? @@ -2598,6 +2774,12 @@ final class PostboxImpl { self.currentStoryTopItemEvents.removeAll() self.currentStoryEvents.removeAll() + if !self.currentUpdatedTypingDrafts.isEmpty { + self.currentUpdatedTypingDrafts.removeAll() + + self.restartTypingDraftExpirationTimerIfNeeded() + } + for table in self.tables { table.beforeCommit() } @@ -2759,7 +2941,7 @@ final class PostboxImpl { let currentData = self.cachedPeerDataTable.get(peerId) if let updatedData = update(peerId, currentData) { self.cachedPeerDataTable.set(id: peerId, data: updatedData) - self.currentUpdatedCachedPeerData[peerId] = updatedData + self.currentUpdatedCachedPeerData[peerId] = (currentData, updatedData) } } } diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index 0098e5b59c..edc5559a5a 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -9,7 +9,7 @@ final class PostboxTransaction { let currentUpdatedPeers: [PeerId: Peer] let currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)] let currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] - let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] + let currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)] let currentUpdatedPeerPresences: [PeerId: PeerPresence] let currentUpdatedPeerChatListEmbeddedStates: Set let currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] @@ -56,6 +56,7 @@ final class PostboxTransaction { let storyItemsEvents: [StoryItemsTable.Event] let currentStoryTopItemEvents: [StoryTopItemsTable.Event] let storyEvents: [StoryTable.Event] + let updatedTypingDrafts: [PeerAndThreadId: PostboxImpl.TypingDraftUpdate] var isEmpty: Bool { if currentUpdatedState != nil { @@ -223,6 +224,9 @@ final class PostboxTransaction { if !self.storyEvents.isEmpty { return false } + if !self.updatedTypingDrafts.isEmpty { + return false + } return true } @@ -236,7 +240,7 @@ final class PostboxTransaction { currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], - currentUpdatedCachedPeerData: [PeerId: CachedPeerData], + currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], @@ -281,7 +285,8 @@ final class PostboxTransaction { storySubscriptionsEvents: [StorySubscriptionsTable.Event], storyItemsEvents: [StoryItemsTable.Event], currentStoryTopItemEvents: [StoryTopItemsTable.Event], - storyEvents: [StoryTable.Event] + storyEvents: [StoryTable.Event], + updatedTypingDrafts: [PeerAndThreadId: PostboxImpl.TypingDraftUpdate] ) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations @@ -337,5 +342,6 @@ final class PostboxTransaction { self.storyItemsEvents = storyItemsEvents self.currentStoryTopItemEvents = currentStoryTopItemEvents self.storyEvents = storyEvents + self.updatedTypingDrafts = updatedTypingDrafts } } diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index 859186b969..7a6bae7121 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -60,7 +60,7 @@ public final class SeedConfiguration { public let initializeChatListWithHole: (topLevel: ChatListHole?, groups: ChatListHole?) public let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]] public let upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]] - public let messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]] + public let messageThreadHoles: (PeerId.Namespace, Int64?) -> [MessageId.Namespace]? public let messageTagsWithSummary: MessageTags public let messageTagsWithThreadSummary: MessageTags public let existingGlobalMessageTags: GlobalMessageTags @@ -77,6 +77,7 @@ public final class SeedConfiguration { public let decodeMessageThreadInfo: (CodableEntry) -> Message.AssociatedThreadInfo? public let decodeAutoremoveTimeout: (CachedPeerData) -> Int32? public let decodeDisplayPeerAsRegularChat: (CachedPeerData) -> Bool + public let decodeAssociatedChatListPeerId: (CachedPeerData) -> PeerId? public let isPeerUpgradeMessage: (Message) -> Bool public let automaticThreadIndexInfo: (PeerId, Int64) -> StoredMessageHistoryThreadInfo? public let customTagsFromAttributes: ([MessageAttribute]) -> [MemoryBuffer] @@ -90,7 +91,7 @@ public final class SeedConfiguration { ), messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]], upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]], - messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]], + messageThreadHoles: @escaping (PeerId.Namespace, Int64?) -> [MessageId.Namespace]?, existingMessageTags: MessageTags, messageTagsWithSummary: MessageTags, messageTagsWithThreadSummary: MessageTags, @@ -108,6 +109,7 @@ public final class SeedConfiguration { decodeMessageThreadInfo: @escaping (CodableEntry) -> Message.AssociatedThreadInfo?, decodeAutoremoveTimeout: @escaping (CachedPeerData) -> Int32?, decodeDisplayPeerAsRegularChat: @escaping (CachedPeerData) -> Bool, + decodeAssociatedChatListPeerId: @escaping (CachedPeerData) -> PeerId?, isPeerUpgradeMessage: @escaping (Message) -> Bool, automaticThreadIndexInfo: @escaping (PeerId, Int64) -> StoredMessageHistoryThreadInfo?, customTagsFromAttributes: @escaping ([MessageAttribute]) -> [MemoryBuffer], @@ -134,6 +136,7 @@ public final class SeedConfiguration { self.decodeMessageThreadInfo = decodeMessageThreadInfo self.decodeAutoremoveTimeout = decodeAutoremoveTimeout self.decodeDisplayPeerAsRegularChat = decodeDisplayPeerAsRegularChat + self.decodeAssociatedChatListPeerId = decodeAssociatedChatListPeerId self.isPeerUpgradeMessage = isPeerUpgradeMessage self.automaticThreadIndexInfo = automaticThreadIndexInfo self.customTagsFromAttributes = customTagsFromAttributes diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 3570dbbf69..ab21dcf429 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -65,6 +65,7 @@ public enum PostboxViewKey: Hashable { case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, customTag: MemoryBuffer?) case historyCustomTagSummariesView(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) case cachedPeerData(peerId: PeerId) + case cachedPeerDataWithMessages(peerId: PeerId) case unreadCounts(items: [UnreadMessageCountsItem]) case combinedReadState(peerId: PeerId, handleThreads: Bool) case peerNotificationSettings(peerIds: Set) @@ -145,6 +146,8 @@ public enum PostboxViewKey: Hashable { hasher.combine(namespace) case let .cachedPeerData(peerId): hasher.combine(peerId) + case let .cachedPeerDataWithMessages(peerId): + hasher.combine(peerId) case .unreadCounts: hasher.combine(5) case .combinedReadState: @@ -320,6 +323,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .cachedPeerDataWithMessages(peerId): + if case .cachedPeerDataWithMessages(peerId) = rhs { + return true + } else { + return false + } case let .unreadCounts(lhsItems): if case let .unreadCounts(rhsItems) = rhs, lhsItems == rhsItems { return true @@ -571,7 +580,9 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost case let .historyCustomTagSummariesView(peerId, threadId, namespace): return MutableMessageHistoryCustomTagSummariesView(postbox: postbox, peerId: peerId, threadId: threadId, namespace: namespace) case let .cachedPeerData(peerId): - return MutableCachedPeerDataView(postbox: postbox, peerId: peerId) + return MutableCachedPeerDataView(postbox: postbox, peerId: peerId, trackAssociatedMessages: false) + case let .cachedPeerDataWithMessages(peerId): + return MutableCachedPeerDataView(postbox: postbox, peerId: peerId, trackAssociatedMessages: true) case let .unreadCounts(items): return MutableUnreadMessageCountsView(postbox: postbox, items: items) case let .combinedReadState(peerId, handleThreads): diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 86d484ae40..bac49d4851 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -321,10 +321,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) let timestamp = self.referenceTimestamp diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 12bfd759ad..96076e48c0 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -470,10 +470,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) diff --git a/submodules/Svg/PublicHeaders/Svg/Svg.h b/submodules/Svg/PublicHeaders/Svg/Svg.h index ec08bd75f8..39080185f3 100755 --- a/submodules/Svg/PublicHeaders/Svg/Svg.h +++ b/submodules/Svg/PublicHeaders/Svg/Svg.h @@ -4,8 +4,28 @@ #import #import +@interface GiftPatternRect : NSObject + +@property (nonatomic) CGPoint center; +@property (nonatomic) CGFloat side; +@property (nonatomic) CGFloat rotation; +@property (nonatomic) CGFloat scale; + +@end + +@interface GiftPatternData : NSObject + +@property (nonatomic) CGSize size; +@property (nonatomic, strong) NSArray * _Nonnull rects; + +@end + NSData * _Nullable prepareSvgImage(NSData * _Nonnull data, bool pattern); + +GiftPatternData * _Nullable getGiftPatternData(NSData * _Nonnull data); + UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit); +UIImage * _Nullable renderPreparedImageWithSymbol(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit, UIImage * _Nullable symbolImage, int32_t modelRectIndex); UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, CGFloat scale, bool opaque); diff --git a/submodules/Svg/Sources/Svg.m b/submodules/Svg/Sources/Svg.m index f425c9c968..d3c4edee41 100755 --- a/submodules/Svg/Sources/Svg.m +++ b/submodules/Svg/Sources/Svg.m @@ -1,255 +1,404 @@ #import - #import "nanosvg.h" #define UIColorRGBA(rgb,a) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:a]) - -CGSize aspectFillSize(CGSize size, CGSize bounds) { - CGFloat scale = MAX(bounds.width / MAX(1.0, size.width), bounds.height / MAX(1.0, size.height)); +#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) + +static inline CGSize aspectFillSize(CGSize size, CGSize bounds) { + if (size.width <= 0 || size.height <= 0) return CGSizeZero; + CGFloat scale = MAX(bounds.width / size.width, bounds.height / size.height); return CGSizeMake(floor(size.width * scale), floor(size.height * scale)); } -CGSize aspectFitSize(CGSize size, CGSize bounds) { - CGFloat scale = MIN(bounds.width / MAX(1.0, size.width), bounds.height / MAX(1.0, size.height)); +static inline CGSize aspectFitSize(CGSize size, CGSize bounds) { + if (size.width <= 0 || size.height <= 0) return CGSizeZero; + CGFloat scale = MIN(bounds.width / size.width, bounds.height / size.height); return CGSizeMake(floor(size.width * scale), floor(size.height * scale)); } -@interface SvgXMLParsingDelegate : NSObject { - NSString *_elementName; - NSString *_currentStyleString; +static inline CGFloat deg2rad(CGFloat deg) { return (deg * (CGFloat)M_PI) / 180.0; } + +static CGSize SVGParseOneTransform(NSString *one, NSString *requiredName) { + NSString *s = [one stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; + if (s.length == 0) return CGSizeZero; + + NSRange paren = [s rangeOfString:@"("]; + if (paren.location == NSNotFound) return CGSizeZero; + + NSString *name = [[s substringToIndex:paren.location] lowercaseString]; + NSString *argsStr = [s substringWithRange:NSMakeRange(paren.location + 1, s.length - paren.location - 2)]; + + NSMutableArray *nums = [NSMutableArray array]; + { + NSScanner *sc = [NSScanner scannerWithString:argsStr]; + while (!sc.isAtEnd) { + double v; + if ([sc scanDouble:&v]) { + [nums addObject:@(v)]; + } else { + sc.scanLocation += 1; + } + } + } + + if ([name isEqualToString:@"translate"] && [name isEqualToString:requiredName]) { + CGFloat tx = nums.count > 0 ? nums[0].doubleValue : 0; + CGFloat ty = nums.count > 1 ? nums[1].doubleValue : 0; + return CGSizeMake(tx, ty); + } else if ([name isEqualToString:@"scale"] && [name isEqualToString:requiredName]) { + CGFloat sx = nums.count > 0 ? nums[0].doubleValue : 1; + CGFloat sy = nums.count > 1 ? nums[1].doubleValue : sx; + return CGSizeMake(sx, sy); + } else if ([name isEqualToString:@"rotate"] && [name isEqualToString:requiredName]) { + CGFloat a = nums.count > 0 ? deg2rad(nums[0].doubleValue) : 0; + return CGSizeMake(a, a); + } + return CGSizeZero; } -@property (nonatomic, strong, readonly) NSMutableDictionary *styles; +static CGAffineTransform SVGParseTransformList(NSString *list) { + if (list.length == 0) { + return CGAffineTransformMake(0.0, 1.0, 1.0, 0.0, 0.0, 0.0); + } + + NSMutableArray *chunks = [NSMutableArray array]; + { + NSMutableString *cur = [NSMutableString string]; + NSInteger depth = 0; + for (NSUInteger i = 0; i < list.length; i++) { + unichar ch = [list characterAtIndex:i]; + [cur appendFormat:@"%C", ch]; + if (ch == '(') depth++; + if (ch == ')') { + depth--; + if (depth == 0) { + [chunks addObject:[cur copy]]; + [cur setString:@""]; + } + } + } + } + CGFloat rotation = 0.0; + CGSize scale = CGSizeMake(1.0, 1.0); + for (NSString *part in chunks) { + CGSize rotationValue = SVGParseOneTransform(part, @"rotate"); + if (ABS(rotationValue.width) > 0.001) { + rotation = rotationValue.width; + } + CGSize scaleValue = SVGParseOneTransform(part, @"scale"); + if (ABS(scaleValue.width) > 0.001 && (ABS(scaleValue.width - 1.0) > 0.001 || ABS(scaleValue.height - 1.0) > 0.001)) { + scale = scaleValue; + } + } + return CGAffineTransformMake(rotation, scale.width, scale.height, 0.0, 0.0, 0.0); +} + +@implementation GiftPatternData @end -@implementation SvgXMLParsingDelegate +@implementation GiftPatternRect + +@end + + +@interface SvgXMLParsingDelegate : NSObject +@property (nonatomic, strong, readonly) NSMutableDictionary *styles; +@property (nonatomic, strong) NSMutableArray *giftRects; +@end + +@implementation SvgXMLParsingDelegate { + NSString *_elementName; + NSMutableString *_currentStyleString; + + bool _inGiftPatterns; +} - (instancetype)init { self = [super init]; - if (self != nil) { + if (self) { _styles = [[NSMutableDictionary alloc] init]; + _currentStyleString = [[NSMutableString alloc] init]; + _giftRects = [NSMutableArray array]; + _inGiftPatterns = false; } return self; } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { - _elementName = elementName; + _elementName = [elementName copy]; + + if ([_elementName isEqualToString:@"g"]) { + NSString *gid = attributeDict[@"id"]; + if ([[gid lowercaseString] isEqualToString:@"giftpatterns"]) { + _inGiftPatterns = true; + } + } else if (_inGiftPatterns && [_elementName isEqualToString:@"rect"]) { + CGFloat x = attributeDict[@"x"] ? attributeDict[@"x"].doubleValue : 0; + CGFloat y = attributeDict[@"y"] ? attributeDict[@"y"].doubleValue : 0; + CGFloat w = attributeDict[@"width"] ? attributeDict[@"width"].doubleValue : 0; + CGFloat h = attributeDict[@"height"] ? attributeDict[@"height"].doubleValue : 0; + + CGFloat side = MAX(w, h); + + CGAffineTransform fakeTransform = CGAffineTransformMake(0.0, 1.0, 1.0, 0.0, 0.0, 0.0); + NSString *rt = attributeDict[@"transform"]; + if (rt.length) { + fakeTransform = SVGParseTransformList(rt); + } + + CGPoint rectCenter = CGPointMake(x + w * 0.5, y + h * 0.5); + + GiftPatternRect *rec = [GiftPatternRect new]; + rec.center = rectCenter; + rec.side = side; + rec.rotation = fakeTransform.a; + rec.scale = fakeTransform.b; + [_giftRects addObject:rec]; + } } - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { - if ([_elementName isEqualToString:@"style"]) { - int currentClassNameStartIndex = -1; - int currentClassContentsStartIndex = -1; - - NSString *currentClassName = nil; - - NSCharacterSet *alphanumeric = [NSCharacterSet alphanumericCharacterSet]; - - for (int i = 0; i < _currentStyleString.length; i++) { - unichar c = [_currentStyleString characterAtIndex:i]; - if (currentClassNameStartIndex != -1) { - if (![alphanumeric characterIsMember:c]) { - currentClassName = [_currentStyleString substringWithRange:NSMakeRange(currentClassNameStartIndex, i - currentClassNameStartIndex)]; - currentClassNameStartIndex = -1; - } - } else if (currentClassContentsStartIndex != -1) { - if (c == '}') { - NSString *classContents = [_currentStyleString substringWithRange:NSMakeRange(currentClassContentsStartIndex, i - currentClassContentsStartIndex)]; - if (currentClassName != nil && classContents != nil) { - _styles[currentClassName] = classContents; - currentClassName = nil; - } - currentClassContentsStartIndex = -1; - } - } - - if (currentClassNameStartIndex == -1 && currentClassContentsStartIndex == -1) { - if (c == '.') { - currentClassNameStartIndex = i + 1; - } else if (c == '{') { - currentClassContentsStartIndex = i + 1; - } - } - } + if ([_elementName isEqualToString:@"style"] && _currentStyleString.length > 0) { + [self parseStyleString:_currentStyleString]; + [_currentStyleString setString:@""]; + } + if ([_elementName isEqualToString:@"g"] && _inGiftPatterns) { + _inGiftPatterns = false; } _elementName = nil; } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { if ([_elementName isEqualToString:@"style"]) { - if (_currentStyleString == nil) { - _currentStyleString = string; - } else { - _currentStyleString = [_currentStyleString stringByAppendingString:string]; + [_currentStyleString appendString:string]; + } +} + +- (void)parseStyleString:(NSString *)styleString { + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern:@"\\.([a-zA-Z0-9_-]+)\\s*\\{([^}]+)\\}" + options:0 error:&error]; + + if (error) { + [self parseStyleStringLegacy:styleString]; + return; + } + + [regex enumerateMatchesInString:styleString options:0 range:NSMakeRange(0, styleString.length) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) { + if (match.numberOfRanges >= 3) { + NSString *className = [styleString substringWithRange:[match rangeAtIndex:1]]; + NSString *classContents = [styleString substringWithRange:[match rangeAtIndex:2]]; + self.styles[className] = [classContents stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + }]; +} + +- (void)parseStyleStringLegacy:(NSString *)styleString { + NSInteger currentClassNameStartIndex = -1; + NSInteger currentClassContentsStartIndex = -1; + NSString *currentClassName = nil; + NSCharacterSet *classNameChars = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"]; + + for (NSInteger i = 0; i < styleString.length; i++) { + unichar c = [styleString characterAtIndex:i]; + + if (currentClassNameStartIndex != -1) { + if (![classNameChars characterIsMember:c]) { + currentClassName = [styleString substringWithRange:NSMakeRange(currentClassNameStartIndex, i - currentClassNameStartIndex)]; + currentClassNameStartIndex = -1; + } + } else if (currentClassContentsStartIndex != -1) { + if (c == '}') { + NSString *classContents = [styleString substringWithRange:NSMakeRange(currentClassContentsStartIndex, i - currentClassContentsStartIndex)]; + if (currentClassName.length > 0 && classContents.length > 0) { + self.styles[currentClassName] = [classContents stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + currentClassName = nil; + currentClassContentsStartIndex = -1; + } + } + + if (currentClassNameStartIndex == -1 && currentClassContentsStartIndex == -1) { + if (c == '.') { + currentClassNameStartIndex = i + 1; + } else if (c == '{') { + currentClassContentsStartIndex = i + 1; + } } } } @end +void renderShape(NSVGshape *shape, CGContextRef context, UIColor *foregroundColor) { + if (shape->fill.type != NSVG_PAINT_NONE) { + CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); + + CGContextBeginPath(context); + bool isFirstPath = true; + + for (NSVGpath *path = shape->paths; path; path = path->next) { + if (!isFirstPath && path->closed) { + CGContextBeginPath(context); + } + + CGContextMoveToPoint(context, path->pts[0], path->pts[1]); + + for (int i = 0; i < path->npts - 1; i += 3) { + float *p = &path->pts[i * 2]; + CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); + } + + if (path->closed) { + CGContextClosePath(context); + } + + isFirstPath = NO; + } + + switch (shape->fillRule) { + case NSVG_FILLRULE_EVENODD: + CGContextEOFillPath(context); + break; + default: + CGContextFillPath(context); + break; + } + } + + if (shape->stroke.type != NSVG_PAINT_NONE && shape->strokeWidth > 0) { + CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); + CGContextSetLineWidth(context, shape->strokeWidth); + CGContextSetMiterLimit(context, shape->miterLimit); + + switch (shape->strokeLineCap) { + case NSVG_CAP_ROUND: CGContextSetLineCap(context, kCGLineCapRound); break; + case NSVG_CAP_SQUARE: CGContextSetLineCap(context, kCGLineCapSquare); break; + default: CGContextSetLineCap(context, kCGLineCapButt); break; + } + + switch (shape->strokeLineJoin) { + case NSVG_JOIN_BEVEL: CGContextSetLineJoin(context, kCGLineJoinBevel); break; + case NSVG_JOIN_ROUND: CGContextSetLineJoin(context, kCGLineJoinRound); break; + default: CGContextSetLineJoin(context, kCGLineJoinMiter); break; + } + + for (NSVGpath *path = shape->paths; path; path = path->next) { + CGContextBeginPath(context); + CGContextMoveToPoint(context, path->pts[0], path->pts[1]); + + for (int i = 0; i < path->npts - 1; i += 3) { + float *p = &path->pts[i * 2]; + CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); + } + + if (path->closed) { + CGContextClosePath(context); + } + CGContextStrokePath(context); + } + } +} + UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor, CGFloat canvasScale, bool opaque) { + if (!data || data.length == 0) return nil; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; - if (parser == nil) { - return nil; - } + if (!parser) return nil; + SvgXMLParsingDelegate *delegate = [[SvgXMLParsingDelegate alloc] init]; parser.delegate = delegate; - [parser parse]; + if (![parser parse]) return nil; NSMutableString *xmlString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (xmlString == nil) { - return nil; - } + if (!xmlString) return nil; for (NSString *styleName in delegate.styles) { NSString *styleValue = delegate.styles[styleName]; - [xmlString replaceOccurrencesOfString:[NSString stringWithFormat:@"class=\"%@\"", styleName] withString:[NSString stringWithFormat:@"style=\"%@\"", styleValue] options:0 range:NSMakeRange(0, xmlString.length)]; + NSString *searchPattern = [NSString stringWithFormat:@"class=\"%@\"", styleName]; + NSString *replacement = [NSString stringWithFormat:@"style=\"%@\"", styleValue]; + [xmlString replaceOccurrencesOfString:searchPattern withString:replacement + options:NSLiteralSearch range:NSMakeRange(0, xmlString.length)]; } - const char *zeroTerminatedData = xmlString.UTF8String; - - NSVGimage *image = nsvgParse((char *)zeroTerminatedData, "px", 96); - if (image == nil || image->width < 1.0f || image->height < 1.0f) { + const char *svgString = xmlString.UTF8String; + NSVGimage *image = nsvgParse((char *)svgString, "px", 96); + if (!image || image->width < 1.0f || image->height < 1.0f) { + if (image) nsvgDelete(image); return nil; } + CGSize originalSize = CGSizeMake(image->width, image->height); if (CGSizeEqualToSize(size, CGSizeZero)) { - size = CGSizeMake(image->width, image->height); + size = originalSize; } - + UIGraphicsBeginImageContextWithOptions(size, opaque, canvasScale); CGContextRef context = UIGraphicsGetCurrentContext(); - if (backgroundColor != nil) { - CGContextSetFillColorWithColor(context, backgroundColor.CGColor); - CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); + if (!context) { + nsvgDelete(image); + return nil; } - CGSize svgSize = CGSizeMake(image->width, image->height); - CGSize drawingSize = aspectFillSize(svgSize, size); + if (backgroundColor) { + CGContextSetFillColorWithColor(context, backgroundColor.CGColor); + CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height)); + } - CGFloat scale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height)); + CGSize drawingSize = aspectFillSize(originalSize, size); + CGFloat scale = MAX(size.width / originalSize.width, size.height / originalSize.height); + CGFloat offsetX = (size.width - drawingSize.width) / (2.0 * scale); + CGFloat offsetY = (size.height - drawingSize.height) / (2.0 * scale); CGContextScaleCTM(context, scale, scale); - CGContextTranslateCTM(context, (size.width - drawingSize.width) / 2.0, (size.height - drawingSize.height) / 2.0); + CGContextTranslateCTM(context, offsetX, offsetY); - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) { - continue; - } + for (NSVGshape *shape = image->shapes; shape; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE) || shape->opacity <= 0) continue; - if (shape->fill.type != NSVG_PAINT_NONE) { - CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); - - bool isFirst = true; - bool hasStartPoint = false; - CGPoint startPoint; - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - if (isFirst) { - CGContextBeginPath(context); - isFirst = false; - hasStartPoint = true; - startPoint.x = path->pts[0]; - startPoint.y = path->pts[1]; - } - CGContextMoveToPoint(context, path->pts[0], path->pts[1]); - for (int i = 0; i < path->npts - 1; i += 3) { - float *p = &path->pts[i * 2]; - CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - } - - if (path->closed) { - if (hasStartPoint) { - hasStartPoint = false; - CGContextAddLineToPoint(context, startPoint.x, startPoint.y); - } - } - } - switch (shape->fillRule) { - case NSVG_FILLRULE_EVENODD: - CGContextEOFillPath(context); - break; - default: - CGContextFillPath(context); - break; - } - } - - if (shape->stroke.type != NSVG_PAINT_NONE) { - CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); - CGContextSetMiterLimit(context, shape->miterLimit); - - CGContextSetLineWidth(context, shape->strokeWidth); - switch (shape->strokeLineCap) { - case NSVG_CAP_BUTT: - CGContextSetLineCap(context, kCGLineCapButt); - break; - case NSVG_CAP_ROUND: - CGContextSetLineCap(context, kCGLineCapRound); - break; - case NSVG_CAP_SQUARE: - CGContextSetLineCap(context, kCGLineCapSquare); - break; - default: - break; - } - switch (shape->strokeLineJoin) { - case NSVG_JOIN_BEVEL: - CGContextSetLineJoin(context, kCGLineJoinBevel); - break; - case NSVG_JOIN_MITER: - CGContextSetLineJoin(context, kCGLineJoinMiter); - break; - case NSVG_JOIN_ROUND: - CGContextSetLineJoin(context, kCGLineJoinRound); - break; - default: - break; - } - - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - CGContextBeginPath(context); - CGContextMoveToPoint(context, path->pts[0], path->pts[1]); - for (int i = 0; i < path->npts - 1; i += 3) { - float *p = &path->pts[i * 2]; - CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - } - - if (path->closed) { - CGContextClosePath(context); - } - CGContextStrokePath(context); - } - } + renderShape(shape, context, foregroundColor); } UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - nsvgDelete(image); return resultImage; } +typedef NS_ENUM(uint8_t, SvgRenderCommand) { + SvgRenderCommandSetFillColorWithOpacity = 1, + SvgRenderCommandSetupStroke = 2, + SvgRenderCommandBeginPath = 3, + SvgRenderCommandMoveTo = 4, + SvgRenderCommandLineTo = 5, + SvgRenderCommandCurveTo = 6, + SvgRenderCommandClosePath = 7, + SvgRenderCommandEOFillPath = 8, + SvgRenderCommandFillPath = 9, + SvgRenderCommandStrokePath = 10, + SvgRenderCommandSetFillColorRGBA = 11, + SvgRenderCommandRectsHeader = 100, + SvgRenderCommandRectsItem = 101 +}; -@interface CGContextCoder : NSObject { +@interface CGContextCoder : NSObject +@property (nonatomic, readonly) NSData *data; +@end + +@implementation CGContextCoder { NSMutableData *_data; } -@property (nonatomic, readonly) NSData *data; - -@end - -@implementation CGContextCoder - - (instancetype)initWithSize:(CGSize)size { self = [super init]; - if (self != nil) { + if (self) { _data = [[NSMutableData alloc] init]; - int32_t intWidth = size.width; - int32_t intHeight = size.height; + int32_t intWidth = (int32_t)size.width; + int32_t intHeight = (int32_t)size.height; [_data appendBytes:&intWidth length:sizeof(intWidth)]; [_data appendBytes:&intHeight length:sizeof(intHeight)]; } @@ -257,451 +406,586 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b } - (void)setFillColorWithOpacity:(CGFloat)opacity { - uint8_t command = 1; - [_data appendBytes:&command length:sizeof(command)]; + uint8_t command = SvgRenderCommandSetFillColorWithOpacity; + uint8_t intOpacity = (uint8_t)(CLAMP(opacity, 0.0, 1.0) * 255.0); - uint8_t intOpacity = opacity * 255.0; + [_data appendBytes:&command length:sizeof(command)]; [_data appendBytes:&intOpacity length:sizeof(intOpacity)]; } -- (void)setupStrokeOpacity:(CGFloat)opacity mitterLimit:(CGFloat)mitterLimit lineWidth:(CGFloat)lineWidth lineCap:(CGLineCap)lineCap lineJoin:(CGLineJoin)lineJoin { - uint8_t command = 2; +- (void)setupStrokeOpacity:(CGFloat)opacity miterLimit:(CGFloat)miterLimit lineWidth:(CGFloat)lineWidth lineCap:(CGLineCap)lineCap lineJoin:(CGLineJoin)lineJoin { + uint8_t command = SvgRenderCommandSetupStroke; + uint8_t intOpacity = (uint8_t)(CLAMP(opacity, 0.0, 1.0) * 255.0); + float floatMiterLimit = (float)miterLimit; + float floatLineWidth = (float)lineWidth; + uint8_t intLineCap = (uint8_t)lineCap; + uint8_t intLineJoin = (uint8_t)lineJoin; + [_data appendBytes:&command length:sizeof(command)]; - - uint8_t intOpacity = opacity * 255.0; [_data appendBytes:&intOpacity length:sizeof(intOpacity)]; - - float floatMitterLimit = mitterLimit; - [_data appendBytes:&floatMitterLimit length:sizeof(floatMitterLimit)]; - - float floatLineWidth = lineWidth; + [_data appendBytes:&floatMiterLimit length:sizeof(floatMiterLimit)]; [_data appendBytes:&floatLineWidth length:sizeof(floatLineWidth)]; - - uint8_t intLineCap = lineCap; [_data appendBytes:&intLineCap length:sizeof(intLineCap)]; - - uint8_t intLineJoin = lineJoin; [_data appendBytes:&intLineJoin length:sizeof(intLineJoin)]; } - (void)beginPath { - uint8_t command = 3; + uint8_t command = SvgRenderCommandBeginPath; [_data appendBytes:&command length:sizeof(command)]; } - (void)moveToPoint:(CGPoint)point { - uint8_t command = 4; - [_data appendBytes:&command length:sizeof(command)]; + uint8_t command = SvgRenderCommandMoveTo; + float x = (float)point.x, y = (float)point.y; - float floatX = point.x; - [_data appendBytes:&floatX length:sizeof(floatX)]; - - float floatY = point.y; - [_data appendBytes:&floatY length:sizeof(floatY)]; + [_data appendBytes:&command length:sizeof(command)]; + [_data appendBytes:&x length:sizeof(x)]; + [_data appendBytes:&y length:sizeof(y)]; } - (void)addLineToPoint:(CGPoint)point { - uint8_t command = 5; - [_data appendBytes:&command length:sizeof(command)]; + uint8_t command = SvgRenderCommandLineTo; + float x = (float)point.x, y = (float)point.y; - float floatX = point.x; - [_data appendBytes:&floatX length:sizeof(floatX)]; - - float floatY = point.y; - [_data appendBytes:&floatY length:sizeof(floatY)]; + [_data appendBytes:&command length:sizeof(command)]; + [_data appendBytes:&x length:sizeof(x)]; + [_data appendBytes:&y length:sizeof(y)]; } - (void)addCurveToPoint:(CGPoint)p1 p2:(CGPoint)p2 p3:(CGPoint)p3 { - uint8_t command = 6; + uint8_t command = SvgRenderCommandCurveTo; + float coords[6] = {(float)p1.x, (float)p1.y, (float)p2.x, (float)p2.y, (float)p3.x, (float)p3.y}; + [_data appendBytes:&command length:sizeof(command)]; - - float floatX1 = p1.x; - [_data appendBytes:&floatX1 length:sizeof(floatX1)]; - - float floatY1 = p1.y; - [_data appendBytes:&floatY1 length:sizeof(floatY1)]; - - float floatX2 = p2.x; - [_data appendBytes:&floatX2 length:sizeof(floatX2)]; - - float floatY2 = p2.y; - [_data appendBytes:&floatY2 length:sizeof(floatY2)]; - - float floatX3 = p3.x; - [_data appendBytes:&floatX3 length:sizeof(floatX3)]; - - float floatY3 = p3.y; - [_data appendBytes:&floatY3 length:sizeof(floatY3)]; + [_data appendBytes:coords length:sizeof(coords)]; } - (void)closePath { - uint8_t command = 7; + uint8_t command = SvgRenderCommandClosePath; [_data appendBytes:&command length:sizeof(command)]; } - (void)eoFillPath { - uint8_t command = 8; + uint8_t command = SvgRenderCommandEOFillPath; [_data appendBytes:&command length:sizeof(command)]; } - (void)fillPath { - uint8_t command = 9; + uint8_t command = SvgRenderCommandFillPath; [_data appendBytes:&command length:sizeof(command)]; } - (void)strokePath { - uint8_t command = 10; + uint8_t command = SvgRenderCommandStrokePath; [_data appendBytes:&command length:sizeof(command)]; } - (void)setFillColor:(uint32_t)color opacity:(CGFloat)opacity { - uint8_t command = 11; + uint8_t command = SvgRenderCommandSetFillColorRGBA; + uint32_t colorWithAlpha = ((uint32_t)(CLAMP(opacity, 0.0, 1.0) * 255.0) << 24) | color; + + [_data appendBytes:&command length:sizeof(command)]; + [_data appendBytes:&colorWithAlpha length:sizeof(colorWithAlpha)]; +} + +- (void)storeGiftPatternRects:(NSArray *)rects { + uint8_t command = SvgRenderCommandRectsHeader; [_data appendBytes:&command length:sizeof(command)]; - color = ((uint32_t)(opacity * 255.0) << 24) | color; - [_data appendBytes:&color length:sizeof(color)]; + uint32_t count = (uint32_t)rects.count; + [_data appendBytes:&count length:sizeof(count)]; + + for (GiftPatternRect *rect in rects) { + uint8_t item = SvgRenderCommandRectsItem; + [_data appendBytes:&item length:sizeof(item)]; + + float payload[5] = { + (float)rect.center.x, (float)rect.center.y, (float)rect.side, (float)rect.rotation, (float)rect.scale + }; + [_data appendBytes:payload length:sizeof(payload)]; + } } @end -UIColor *colorWithBGRA(uint32_t bgra) -{ - return [[UIColor alloc] initWithRed:(((bgra) & 0xff) / 255.0f) green:(((bgra >> 8) & 0xff) / 255.0f) blue:(((bgra >> 16) & 0xff) / 255.0f) alpha:(((bgra >> 24) & 0xff) / 255.0f)]; +static inline UIColor *colorWithBGRA(uint32_t bgra) { + return [[UIColor alloc] initWithRed:(((bgra) & 0xff) / 255.0f) + green:(((bgra >> 8) & 0xff) / 255.0f) + blue:(((bgra >> 16) & 0xff) / 255.0f) + alpha:(((bgra >> 24) & 0xff) / 255.0f)]; } -UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale, bool fit) { - NSDate *startTime = [NSDate date]; - - UIColor *foregroundColor = [UIColor whiteColor]; - - int32_t ptr = 0; - int32_t width; - int32_t height; - - if (data.length < 4 * 2) { +bool processRenderCommand(uint8_t cmd, NSData *data, NSUInteger *ptr, CGContextRef *context, UIColor *foregroundColor) { + switch (cmd) { + case SvgRenderCommandSetFillColorWithOpacity: { + if (*ptr + 1 > data.length) return NO; + uint8_t opacity; + [data getBytes:&opacity range:NSMakeRange(*ptr, sizeof(opacity))]; + *ptr += sizeof(opacity); + if (context != nil) { + CGContextSetFillColorWithColor(*context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor); + } + break; + } + + case SvgRenderCommandSetupStroke: { + if (*ptr + 10 > data.length) return NO; + uint8_t opacity; + float miterLimit, lineWidth; + uint8_t lineCap, lineJoin; + + [data getBytes:&opacity range:NSMakeRange(*ptr, sizeof(opacity))]; + *ptr += sizeof(opacity); + [data getBytes:&miterLimit range:NSMakeRange(*ptr, sizeof(miterLimit))]; + *ptr += sizeof(miterLimit); + [data getBytes:&lineWidth range:NSMakeRange(*ptr, sizeof(lineWidth))]; + *ptr += sizeof(lineWidth); + [data getBytes:&lineCap range:NSMakeRange(*ptr, sizeof(lineCap))]; + *ptr += sizeof(lineCap); + [data getBytes:&lineJoin range:NSMakeRange(*ptr, sizeof(lineJoin))]; + *ptr += sizeof(lineJoin); + + if (context != nil) { + CGContextSetStrokeColorWithColor(*context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor); + CGContextSetMiterLimit(*context, miterLimit); + CGContextSetLineWidth(*context, lineWidth); + CGContextSetLineCap(*context, (CGLineCap)lineCap); + CGContextSetLineJoin(*context, (CGLineJoin)lineJoin); + } + break; + } + + case SvgRenderCommandBeginPath: + if (context != nil) { + CGContextBeginPath(*context); + } + break; + + case SvgRenderCommandMoveTo: { + if (*ptr + 8 > data.length) return NO; + float x, y; + [data getBytes:&x range:NSMakeRange(*ptr, sizeof(x))]; + *ptr += sizeof(x); + [data getBytes:&y range:NSMakeRange(*ptr, sizeof(y))]; + *ptr += sizeof(y); + if (context != nil) { + CGContextMoveToPoint(*context, x, y); + } + break; + } + + case SvgRenderCommandLineTo: { + if (*ptr + 8 > data.length) return NO; + float x, y; + [data getBytes:&x range:NSMakeRange(*ptr, sizeof(x))]; + *ptr += sizeof(x); + [data getBytes:&y range:NSMakeRange(*ptr, sizeof(y))]; + *ptr += sizeof(y); + if (context != nil) { + CGContextAddLineToPoint(*context, x, y); + } + break; + } + + case SvgRenderCommandCurveTo: { + if (*ptr + 24 > data.length) return NO; + float coords[6]; + [data getBytes:coords range:NSMakeRange(*ptr, sizeof(coords))]; + *ptr += sizeof(coords); + if (context != nil) { + CGContextAddCurveToPoint(*context, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); + } + break; + } + + case SvgRenderCommandClosePath: + if (context != nil) { + CGContextClosePath(*context); + } + break; + + case SvgRenderCommandEOFillPath: + if (context != nil) { + CGContextEOFillPath(*context); + } + break; + + case SvgRenderCommandFillPath: + if (context != nil) { + CGContextFillPath(*context); + } + break; + + case SvgRenderCommandStrokePath: + if (context != nil) { + CGContextStrokePath(*context); + } + break; + + case SvgRenderCommandSetFillColorRGBA: { + if (*ptr + 4 > data.length) return NO; + uint32_t bgra; + [data getBytes:&bgra range:NSMakeRange(*ptr, sizeof(bgra))]; + *ptr += sizeof(bgra); + if (context != nil) { + CGContextSetFillColorWithColor(*context, colorWithBGRA(bgra).CGColor); + } + break; + } + case SvgRenderCommandRectsHeader: + break; + case SvgRenderCommandRectsItem: + break; + default: + return false; + } + return true; +} + +GiftPatternData *getGiftPatternData(NSData * _Nonnull data) { + if (!data || data.length < 8) { return nil; } - + + NSUInteger ptr = 0; + int32_t width, height; [data getBytes:&width range:NSMakeRange(ptr, sizeof(width))]; ptr += sizeof(width); [data getBytes:&height range:NSMakeRange(ptr, sizeof(height))]; ptr += sizeof(height); - if (CGSizeEqualToSize(size, CGSizeZero)) { - size = CGSizeMake(width, height); - } - - bool isTransparent = [backgroundColor isEqual:[UIColor clearColor]]; - - CGSize svgSize = CGSizeMake(width, height); - CGSize drawingSize; - if (fit) { - drawingSize = aspectFitSize(svgSize, size); - size = drawingSize; - } else { - drawingSize = aspectFillSize(svgSize, size); - } - - UIGraphicsBeginImageContextWithOptions(size, !isTransparent, scale); - CGContextRef context = UIGraphicsGetCurrentContext(); - if (isTransparent) { - CGContextClearRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); - } else { - CGContextSetFillColorWithColor(context, backgroundColor.CGColor); - CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); - } - - - - CGFloat renderScale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height)); - - CGContextScaleCTM(context, renderScale, renderScale); - CGContextTranslateCTM(context, (size.width - drawingSize.width) / 2.0, (size.height - drawingSize.height) / 2.0); + NSMutableArray *rects = [[NSMutableArray alloc] init]; while (ptr < data.length) { + if (ptr + 1 > data.length) break; + uint8_t cmd; [data getBytes:&cmd range:NSMakeRange(ptr, sizeof(cmd))]; ptr += sizeof(cmd); - switch (cmd) { - case 1: - { - uint8_t opacity; - [data getBytes:&opacity range:NSMakeRange(ptr, sizeof(opacity))]; - ptr += sizeof(opacity); - CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor); - } - break; - - case 2: - { - uint8_t opacity; - [data getBytes:&opacity range:NSMakeRange(ptr, sizeof(opacity))]; - ptr += sizeof(opacity); - CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent:opacity / 255.0].CGColor); - - float mitterLimit; - [data getBytes:&mitterLimit range:NSMakeRange(ptr, sizeof(mitterLimit))]; - ptr += sizeof(mitterLimit); - CGContextSetMiterLimit(context, mitterLimit); - - float lineWidth; - [data getBytes:&lineWidth range:NSMakeRange(ptr, sizeof(lineWidth))]; - ptr += sizeof(lineWidth); - CGContextSetLineWidth(context, lineWidth); - - uint8_t lineCap; - [data getBytes:&lineCap range:NSMakeRange(ptr, sizeof(lineCap))]; - ptr += sizeof(lineCap); - CGContextSetLineCap(context, lineCap); - - uint8_t lineJoin; - [data getBytes:&lineJoin range:NSMakeRange(ptr, sizeof(lineJoin))]; - ptr += sizeof(lineJoin); - CGContextSetLineCap(context, lineJoin); - } - break; - - case 3: - { - CGContextBeginPath(context); - } - break; - - case 4: - { - float x; - [data getBytes:&x range:NSMakeRange(ptr, sizeof(x))]; - ptr += sizeof(x); - - float y; - [data getBytes:&y range:NSMakeRange(ptr, sizeof(y))]; - ptr += sizeof(y); - - CGContextMoveToPoint(context, x, y); - } - break; - - case 5: - { - float x; - [data getBytes:&x range:NSMakeRange(ptr, sizeof(x))]; - ptr += sizeof(x); - - float y; - [data getBytes:&y range:NSMakeRange(ptr, sizeof(y))]; - ptr += sizeof(y); - - CGContextAddLineToPoint(context, x, y); - } - break; - - case 6: - { - float x1; - [data getBytes:&x1 range:NSMakeRange(ptr, sizeof(x1))]; - ptr += sizeof(x1); - - float y1; - [data getBytes:&y1 range:NSMakeRange(ptr, sizeof(y1))]; - ptr += sizeof(y1); - - float x2; - [data getBytes:&x2 range:NSMakeRange(ptr, sizeof(x2))]; - ptr += sizeof(x2); - - float y2; - [data getBytes:&y2 range:NSMakeRange(ptr, sizeof(y2))]; - ptr += sizeof(y2); - - float x3; - [data getBytes:&x3 range:NSMakeRange(ptr, sizeof(x3))]; - ptr += sizeof(x3); - - float y3; - [data getBytes:&y3 range:NSMakeRange(ptr, sizeof(y3))]; - ptr += sizeof(y3); - - CGContextAddCurveToPoint(context, x1, y1, x2, y2, x3, y3); - } - break; - - case 7: - { - CGContextClosePath(context); - } - break; - - case 8: - { - CGContextEOFillPath(context); - } - break; + if (cmd == SvgRenderCommandRectsHeader) { + if (ptr + sizeof(uint32_t) > data.length) break; + uint32_t count = 0; + [data getBytes:&count range:NSMakeRange(ptr, sizeof(count))]; + ptr += sizeof(count); - case 9: - { - CGContextFillPath(context); - } - break; + for (uint32_t i = 0; i < count; i++) { + if (ptr + 1 > data.length) { ptr = data.length; break; } + uint8_t itemCmd = 0; + [data getBytes:&itemCmd range:NSMakeRange(ptr, sizeof(itemCmd))]; + ptr += sizeof(itemCmd); + if (itemCmd != SvgRenderCommandRectsItem) { + ptr = data.length; + break; + } - case 10: - { - CGContextStrokePath(context); - } - break; - case 11: - { - uint32_t bgra; - [data getBytes:&bgra range:NSMakeRange(ptr, sizeof(bgra))]; - ptr += sizeof(bgra); + if (ptr + sizeof(float) * 5 > data.length) { + ptr = data.length; + break; + } + float payload[5]; + [data getBytes:payload range:NSMakeRange(ptr, sizeof(payload))]; + ptr += sizeof(payload); - CGContextSetFillColorWithColor(context, colorWithBGRA(bgra).CGColor); - CGContextStrokePath(context); + GiftPatternRect *rect = [[GiftPatternRect alloc] init]; + rect.center = CGPointMake(payload[0], payload[1]); + rect.side = payload[2]; + rect.rotation = payload[3]; + rect.scale = payload[4]; + [rects addObject:rect]; } - default: - break; + continue; + } + + if (!processRenderCommand(cmd, data, &ptr, nil, [UIColor whiteColor])) { + break; } } + + GiftPatternData *patternData = [[GiftPatternData alloc] init]; + patternData.size = CGSizeMake(width, height); + patternData.rects = rects; + return patternData; +} + +UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale, bool fit) { + return renderPreparedImageWithSymbol(data, size, backgroundColor, scale, fit, nil, -1); +} + +UIImage * _Nullable renderPreparedImageWithSymbol(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale, bool fit, UIImage * _Nullable symbolImage, int32_t modelRectIndex) { + if (!data || data.length < 8) { + return nil; + } + + CFTimeInterval startTime = CACurrentMediaTime(); + UIColor *foregroundColor = [UIColor whiteColor]; + + NSUInteger ptr = 0; + int32_t width, height; + [data getBytes:&width range:NSMakeRange(ptr, sizeof(width))]; + ptr += sizeof(width); + [data getBytes:&height range:NSMakeRange(ptr, sizeof(height))]; + ptr += sizeof(height); + + if (width <= 0 || height <= 0) return nil; + + CGSize svgSize = CGSizeMake(width, height); + if (CGSizeEqualToSize(size, CGSizeZero)) { + size = svgSize; + } + + bool isTransparent = [backgroundColor isEqual:[UIColor clearColor]]; + CGSize drawingSize = fit ? aspectFitSize(svgSize, size) : aspectFillSize(svgSize, size); + + if (fit) size = drawingSize; + + UIGraphicsBeginImageContextWithOptions(size, !isTransparent, scale); + CGContextRef context = UIGraphicsGetCurrentContext(); + if (!context) { + UIGraphicsEndImageContext(); + return nil; + } + + if (isTransparent) { + CGContextClearRect(context, CGRectMake(0, 0, size.width, size.height)); + } else { + CGContextSetFillColorWithColor(context, backgroundColor.CGColor); + CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height)); + } + + CGFloat renderScale = fit ? MIN(size.width / svgSize.width, size.height / svgSize.height) : MAX(size.width / svgSize.width, size.height / svgSize.height); + + CGContextScaleCTM(context, renderScale, renderScale); + CGContextTranslateCTM(context, (size.width - drawingSize.width) / (2.0 * renderScale), (size.height - drawingSize.height) / (2.0 * renderScale)); + + NSMutableArray *rects = symbolImage ? [[NSMutableArray alloc] init] : nil; + + while (ptr < data.length) { + if (ptr + 1 > data.length) break; + + uint8_t cmd; + [data getBytes:&cmd range:NSMakeRange(ptr, sizeof(cmd))]; + ptr += sizeof(cmd); + + if (cmd == SvgRenderCommandRectsHeader) { + if (ptr + sizeof(uint32_t) > data.length) break; + uint32_t count = 0; + [data getBytes:&count range:NSMakeRange(ptr, sizeof(count))]; + ptr += sizeof(count); + for (uint32_t i = 0; i < count; i++) { + if (ptr + 1 > data.length) { ptr = data.length; break; } + uint8_t itemCmd = 0; + [data getBytes:&itemCmd range:NSMakeRange(ptr, sizeof(itemCmd))]; + ptr += sizeof(itemCmd); + if (itemCmd != SvgRenderCommandRectsItem) { ptr = data.length; break; } + + if (ptr + sizeof(float) * 5 > data.length) { ptr = data.length; break; } + float payload[5]; + [data getBytes:payload range:NSMakeRange(ptr, sizeof(payload))]; + ptr += sizeof(payload); + + if (rects) { + GiftPatternRect *rect = [[GiftPatternRect alloc] init]; + rect.center = CGPointMake(payload[0], payload[1]); + rect.side = payload[2]; + rect.rotation = payload[3]; + rect.scale = payload[4]; + [rects addObject:rect]; + } + } + continue; + } + + if (!processRenderCommand(cmd, data, &ptr, &context, foregroundColor)) { + break; + } + } + + if (symbolImage && rects.count > 0) { + int32_t index = 0; + + NSMutableArray *filteredRects = [[NSMutableArray alloc] init]; + for (GiftPatternRect *rect in rects) { + if (rect.center.y > height * 0.1 && rect.center.y < height * 0.9) { + [filteredRects addObject:rect]; + } + } + modelRectIndex = modelRectIndex % (int32_t)filteredRects.count; + + for (GiftPatternRect *rect in filteredRects) { + if (index != modelRectIndex) { + CGContextSaveGState(context); + + CGContextTranslateCTM(context, rect.center.x, rect.center.y); + CGContextRotateCTM(context, rect.rotation); + + CGFloat symbolAspectRatio = (symbolImage.size.height > 0.0) ? (symbolImage.size.width / symbolImage.size.height) : 1.0; + CGFloat drawWidth = rect.side; + CGFloat drawHeight = rect.side; + + if (symbolAspectRatio > 1.0) { + drawHeight = drawWidth / symbolAspectRatio; + } else { + drawWidth = drawHeight * symbolAspectRatio; + } + + CGRect symbolRect = CGRectMake(-drawWidth * 0.5, -drawHeight * 0.5, drawWidth, drawHeight); + [symbolImage drawInRect:symbolRect blendMode:kCGBlendModeNormal alpha:1.0]; + + CGContextRestoreGState(context); + } + index += 1; + } + } + UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - double deltaTime = -1.0f * [startTime timeIntervalSinceNow]; - printf("drawingTime %fx%f = %f\n", size.width, size.height, deltaTime); + CFTimeInterval deltaTime = CACurrentMediaTime() - startTime; + NSLog(@"Render time %.0fx%.0f = %.4f seconds", size.width, size.height, deltaTime); return resultImage; } -NSData * _Nullable prepareSvgImage(NSData * _Nonnull data, bool template) { - NSDate *startTime = [NSDate date]; +void processShape(NSVGshape *shape, CGContextCoder *context, bool template) { + if (shape->fill.type != NSVG_PAINT_NONE) { + if (template) { + [context setFillColorWithOpacity:shape->opacity]; + } else { + [context setFillColor:shape->fill.color opacity:shape->opacity]; + } + + [context beginPath]; + BOOL hasStartPoint = false; + CGPoint startPoint = CGPointZero; + + for (NSVGpath *path = shape->paths; path; path = path->next) { + if (!hasStartPoint) { + hasStartPoint = true; + startPoint = CGPointMake(path->pts[0], path->pts[1]); + } + + [context moveToPoint:CGPointMake(path->pts[0], path->pts[1])]; + + for (int i = 0; i < path->npts - 1; i += 3) { + float *p = &path->pts[i * 2]; + [context addCurveToPoint:CGPointMake(p[2], p[3]) p2:CGPointMake(p[4], p[5]) p3:CGPointMake(p[6], p[7])]; + } + + if (path->closed && hasStartPoint) { + [context addLineToPoint:startPoint]; + hasStartPoint = NO; + } + } + + switch (shape->fillRule) { + case NSVG_FILLRULE_EVENODD: + [context eoFillPath]; + break; + default: + [context fillPath]; + break; + } + } + + if (shape->stroke.type != NSVG_PAINT_NONE && shape->strokeWidth > 0) { + CGLineCap lineCap = kCGLineCapButt; + CGLineJoin lineJoin = kCGLineJoinMiter; + + switch (shape->strokeLineCap) { + case NSVG_CAP_ROUND: lineCap = kCGLineCapRound; break; + case NSVG_CAP_SQUARE: lineCap = kCGLineCapSquare; break; + default: break; + } + + switch (shape->strokeLineJoin) { + case NSVG_JOIN_BEVEL: lineJoin = kCGLineJoinBevel; break; + case NSVG_JOIN_ROUND: lineJoin = kCGLineJoinRound; break; + default: break; + } + + [context setupStrokeOpacity:shape->opacity + miterLimit:shape->miterLimit + lineWidth:shape->strokeWidth + lineCap:lineCap + lineJoin:lineJoin]; + + for (NSVGpath *path = shape->paths; path; path = path->next) { + [context beginPath]; + [context moveToPoint:CGPointMake(path->pts[0], path->pts[1])]; + + for (int i = 0; i < path->npts - 1; i += 3) { + float *p = &path->pts[i * 2]; + [context addCurveToPoint:CGPointMake(p[2], p[3]) + p2:CGPointMake(p[4], p[5]) + p3:CGPointMake(p[6], p[7])]; + } + + if (path->closed) { + [context closePath]; + } + [context strokePath]; + } + } +} + +NSData * _Nullable prepareSvgImage(NSData * _Nonnull data, BOOL template) { + if (!data || data.length == 0) return nil; + + CFTimeInterval startTime = CACurrentMediaTime(); NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; - if (parser == nil) { - return nil; - } + if (!parser) return nil; + SvgXMLParsingDelegate *delegate = [[SvgXMLParsingDelegate alloc] init]; parser.delegate = delegate; - [parser parse]; + if (![parser parse]) return nil; NSMutableString *xmlString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (xmlString == nil) { - return nil; - } + if (!xmlString) return nil; for (NSString *styleName in delegate.styles) { NSString *styleValue = delegate.styles[styleName]; - [xmlString replaceOccurrencesOfString:[NSString stringWithFormat:@"class=\"%@\"", styleName] withString:[NSString stringWithFormat:@"style=\"%@\"", styleValue] options:0 range:NSMakeRange(0, xmlString.length)]; + NSString *searchPattern = [NSString stringWithFormat:@"class=\"%@\"", styleName]; + NSString *replacement = [NSString stringWithFormat:@"style=\"%@\"", styleValue]; + [xmlString replaceOccurrencesOfString:searchPattern withString:replacement + options:NSLiteralSearch range:NSMakeRange(0, xmlString.length)]; } - const char *zeroTerminatedData = xmlString.UTF8String; + { + NSError *err = nil; + NSRegularExpression *gx = [NSRegularExpression regularExpressionWithPattern:@"]*\\bid\\s*=\\s*[\"']GiftPatterns[\"'][^>]*>[\\s\\S]*?" options:NSRegularExpressionCaseInsensitive error:&err]; + if (gx) { + xmlString = [[gx stringByReplacingMatchesInString:xmlString options:0 range:NSMakeRange(0, xmlString.length) withTemplate:@""] mutableCopy]; + } + } - NSVGimage *image = nsvgParse((char *)zeroTerminatedData, "px", 96); - if (image == nil || image->width < 1.0f || image->height < 1.0f) { + const char *svgString = xmlString.UTF8String; + NSVGimage *image = nsvgParse((char *)svgString, "px", 96); + if (!image || image->width < 1.0f || image->height < 1.0f) { + if (image) nsvgDelete(image); return nil; } - double deltaTime = -1.0f * [startTime timeIntervalSinceNow]; - printf("parseTime = %f\n", deltaTime); + CFTimeInterval parseTime = CACurrentMediaTime() - startTime; + NSLog(@"Parse time: %.4f seconds", parseTime); - startTime = [NSDate date]; - + startTime = CACurrentMediaTime(); CGContextCoder *context = [[CGContextCoder alloc] initWithSize:CGSizeMake(image->width, image->height)]; - - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) { - continue; - } + + for (NSVGshape *shape = image->shapes; shape; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE) || shape->opacity <= 0) continue; - if (shape->fill.type != NSVG_PAINT_NONE) { - if (template) { - [context setFillColorWithOpacity:shape->opacity]; - } else { - [context setFillColor:shape->fill.color opacity:shape->opacity]; - } - - bool isFirst = true; - bool hasStartPoint = false; - CGPoint startPoint; - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - if (isFirst) { - [context beginPath]; - - isFirst = false; - hasStartPoint = true; - startPoint.x = path->pts[0]; - startPoint.y = path->pts[1]; - } - [context moveToPoint:CGPointMake(path->pts[0], path->pts[1])]; - for (int i = 0; i < path->npts - 1; i += 3) { - float *p = &path->pts[i * 2]; - [context addCurveToPoint:CGPointMake(p[2], p[3]) p2:CGPointMake(p[4], p[5]) p3:CGPointMake(p[6], p[7])]; - } - - if (path->closed) { - if (hasStartPoint) { - hasStartPoint = false; - [context addLineToPoint:startPoint]; - } - } - } - switch (shape->fillRule) { - case NSVG_FILLRULE_EVENODD: - [context eoFillPath]; - break; - default: - [context fillPath]; - break; - } - } - - if (shape->stroke.type != NSVG_PAINT_NONE) { - CGLineCap lineCap = kCGLineCapButt; - CGLineJoin lineJoin = kCGLineJoinMiter; - switch (shape->strokeLineCap) { - case NSVG_CAP_BUTT: - lineCap = kCGLineCapButt; - break; - case NSVG_CAP_ROUND: - lineCap = kCGLineCapRound; - break; - case NSVG_CAP_SQUARE: - lineCap = kCGLineCapSquare; - break; - default: - break; - } - switch (shape->strokeLineJoin) { - case NSVG_JOIN_BEVEL: - lineJoin = kCGLineJoinBevel; - break; - case NSVG_JOIN_MITER: - lineJoin = kCGLineJoinMiter; - break; - case NSVG_JOIN_ROUND: - lineJoin = kCGLineJoinRound; - break; - default: - break; - } - - [context setupStrokeOpacity:shape->opacity mitterLimit:shape->miterLimit lineWidth:shape->strokeWidth lineCap:lineCap lineJoin:lineJoin]; - - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - [context beginPath]; - [context moveToPoint:CGPointMake(path->pts[0], path->pts[1])]; - for (int i = 0; i < path->npts - 1; i += 3) { - float *p = &path->pts[i * 2]; - [context addCurveToPoint:CGPointMake(p[2], p[3]) p2:CGPointMake(p[4], p[5]) p3:CGPointMake(p[6], p[7])]; - } - - if (path->closed) { - [context closePath]; - } - [context strokePath]; - } - } + processShape(shape, context, template); } nsvgDelete(image); + + [context storeGiftPatternRects:delegate.giftRects]; + + CFTimeInterval prepTime = CACurrentMediaTime() - startTime; + NSLog(@"Preparation time: %.4f seconds", prepTime); + return context.data; } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index bd6c07b708..ad00f2f471 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -204,7 +204,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1605510357] = { return Api.ChatAdminRights.parse_chatAdminRights($0) } dict[-219353309] = { return Api.ChatAdminWithInvites.parse_chatAdminWithInvites($0) } dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) } - dict[-529258018] = { return Api.ChatFull.parse_channelFull($0) } + dict[-455036259] = { return Api.ChatFull.parse_channelFull($0) } dict[640893467] = { return Api.ChatFull.parse_chatFull($0) } dict[1553807106] = { return Api.ChatInvite.parse_chatInvite($0) } dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) } @@ -221,6 +221,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1385335754] = { return Api.ChatReactions.parse_chatReactionsAll($0) } dict[-352570692] = { return Api.ChatReactions.parse_chatReactionsNone($0) } dict[1713193015] = { return Api.ChatReactions.parse_chatReactionsSome($0) } + dict[-1008731132] = { return Api.ChatTheme.parse_chatTheme($0) } + dict[878246344] = { return Api.ChatTheme.parse_chatThemeUniqueGift($0) } dict[-1390068360] = { return Api.CodeSettings.parse_codeSettings($0) } dict[-870702050] = { return Api.Config.parse_config($0) } dict[-849058964] = { return Api.ConnectedBot.parse_connectedBot($0) } @@ -344,6 +346,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) } dict[480546647] = { return Api.InputChatPhoto.parse_inputChatPhotoEmpty($0) } dict[-1110593856] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) } + dict[-918689444] = { return Api.InputChatTheme.parse_inputChatTheme($0) } + dict[-2094627709] = { return Api.InputChatTheme.parse_inputChatThemeEmpty($0) } + dict[-2014978076] = { return Api.InputChatTheme.parse_inputChatThemeUniqueGift($0) } dict[-203367885] = { return Api.InputChatlist.parse_inputChatlistDialogFilter($0) } dict[-1736378792] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordEmpty($0) } dict[-763367294] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordSRP($0) } @@ -388,6 +393,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-625298705] = { return Api.InputInvoice.parse_inputInvoicePremiumGiftStars($0) } dict[-1020867857] = { return Api.InputInvoice.parse_inputInvoiceSlug($0) } dict[-396206446] = { return Api.InputInvoice.parse_inputInvoiceStarGift($0) } + dict[-1710536520] = { return Api.InputInvoice.parse_inputInvoiceStarGiftPrepaidUpgrade($0) } dict[-1012968668] = { return Api.InputInvoice.parse_inputInvoiceStarGiftResale($0) } dict[1247763417] = { return Api.InputInvoice.parse_inputInvoiceStarGiftTransfer($0) } dict[1300335965] = { return Api.InputInvoice.parse_inputInvoiceStarGiftUpgrade($0) } @@ -495,7 +501,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1502273946] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumSubscription($0) } dict[494149367] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentStarsGift($0) } dict[1964968186] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentStarsGiveaway($0) } - dict[-572715178] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentStarsTopup($0) } + dict[-106780981] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentStarsTopup($0) } dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) } dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) } dict[-1881255857] = { return Api.InputThemeSettings.parse_inputThemeSettings($0) } @@ -599,10 +605,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1200788123] = { return Api.MessageAction.parse_messageActionScreenshotTaken($0) } dict[-648257196] = { return Api.MessageAction.parse_messageActionSecureValuesSent($0) } dict[455635795] = { return Api.MessageAction.parse_messageActionSecureValuesSentMe($0) } - dict[-1434950843] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) } + dict[-1189364422] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) } dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) } dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) } - dict[1192749220] = { return Api.MessageAction.parse_messageActionStarGift($0) } + dict[-229775366] = { return Api.MessageAction.parse_messageActionStarGift($0) } dict[888627955] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) } dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) } dict[-293988970] = { return Api.MessageAction.parse_messageActionSuggestedPostApproval($0) } @@ -815,6 +821,14 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1103656293] = { return Api.PrivacyRule.parse_privacyValueDisallowChatParticipants($0) } dict[-125240806] = { return Api.PrivacyRule.parse_privacyValueDisallowContacts($0) } dict[-463335103] = { return Api.PrivacyRule.parse_privacyValueDisallowUsers($0) } + dict[-1422681088] = { return Api.ProfileTab.parse_profileTabFiles($0) } + dict[-1564412267] = { return Api.ProfileTab.parse_profileTabGifs($0) } + dict[1296815210] = { return Api.ProfileTab.parse_profileTabGifts($0) } + dict[-748329831] = { return Api.ProfileTab.parse_profileTabLinks($0) } + dict[1925597525] = { return Api.ProfileTab.parse_profileTabMedia($0) } + dict[-1624780178] = { return Api.ProfileTab.parse_profileTabMusic($0) } + dict[-1181952362] = { return Api.ProfileTab.parse_profileTabPosts($0) } + dict[-461960914] = { return Api.ProfileTab.parse_profileTabVoice($0) } dict[32685898] = { return Api.PublicForward.parse_publicForwardMessage($0) } dict[-302797360] = { return Api.PublicForward.parse_publicForwardStory($0) } dict[110563371] = { return Api.QuickReply.parse_quickReply($0) } @@ -880,7 +894,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1681948327] = { return Api.SavedDialog.parse_monoForumDialog($0) } dict[-1115174036] = { return Api.SavedDialog.parse_savedDialog($0) } dict[-881854424] = { return Api.SavedReactionTag.parse_savedReactionTag($0) } - dict[514213599] = { return Api.SavedStarGift.parse_savedStarGift($0) } + dict[430552434] = { return Api.SavedStarGift.parse_savedStarGift($0) } dict[1040931690] = { return Api.SearchPostsFlood.parse_searchPostsFlood($0) } dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) } dict[2137295719] = { return Api.SearchResultsPosition.parse_searchResultPosition($0) } @@ -944,8 +958,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[2109703795] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) } dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) } dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) } - dict[12386139] = { return Api.StarGift.parse_starGift($0) } - dict[975654224] = { return Api.StarGift.parse_starGiftUnique($0) } + dict[-2136190013] = { return Api.StarGift.parse_starGift($0) } + dict[468707429] = { return Api.StarGift.parse_starGiftUnique($0) } dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) } dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) } dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) } @@ -1181,7 +1195,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[34280482] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } - dict[2120470047] = { return Api.UserFull.parse_userFull($0) } + dict[-982010451] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) } dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) } @@ -1216,6 +1230,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) } dict[1279133341] = { return Api.account.AutoSaveSettings.parse_autoSaveSettings($0) } dict[-331111727] = { return Api.account.BusinessChatLinks.parse_businessChatLinks($0) } + dict[373835863] = { return Api.account.ChatThemes.parse_chatThemes($0) } + dict[-535699004] = { return Api.account.ChatThemes.parse_chatThemesNotModified($0) } dict[400029819] = { return Api.account.ConnectedBots.parse_connectedBots($0) } dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) } dict[731303195] = { return Api.account.EmailVerified.parse_emailVerified($0) } @@ -1231,6 +1247,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-383330754] = { return Api.account.ResetPasswordResult.parse_resetPasswordOk($0) } dict[-370148227] = { return Api.account.ResetPasswordResult.parse_resetPasswordRequestedWait($0) } dict[-1708937439] = { return Api.account.ResolvedBusinessChatLinks.parse_resolvedBusinessChatLinks($0) } + dict[-1718786506] = { return Api.account.SavedMusicIds.parse_savedMusicIds($0) } + dict[1338514798] = { return Api.account.SavedMusicIds.parse_savedMusicIdsNotModified($0) } dict[-1222230163] = { return Api.account.SavedRingtone.parse_savedRingtone($0) } dict[523271863] = { return Api.account.SavedRingtone.parse_savedRingtoneConverted($0) } dict[-1041683259] = { return Api.account.SavedRingtones.parse_savedRingtones($0) } @@ -1257,7 +1275,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) } dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) } dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) } - dict[-674301568] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) } + dict[-677184263] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) } dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) } dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } @@ -1418,8 +1436,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[870003448] = { return Api.messages.TranslatedText.parse_translateResult($0) } dict[1218005070] = { return Api.messages.VotesList.parse_votesList($0) } dict[-44166467] = { return Api.messages.WebPage.parse_webPage($0) } - dict[-1254192351] = { return Api.messages.WebPagePreview.parse_webPagePreview($0) } + dict[-1936029524] = { return Api.messages.WebPagePreview.parse_webPagePreview($0) } dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) } + dict[-706379148] = { return Api.payments.CheckCanSendGiftResult.parse_checkCanSendGiftResultFail($0) } + dict[927967149] = { return Api.payments.CheckCanSendGiftResult.parse_checkCanSendGiftResultOk($0) } dict[675942550] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) } dict[-1730811363] = { return Api.payments.ConnectedStarRefBots.parse_connectedStarRefBots($0) } dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) } @@ -1446,7 +1466,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[497778871] = { return Api.payments.StarsRevenueWithdrawalUrl.parse_starsRevenueWithdrawalUrl($0) } dict[1822222573] = { return Api.payments.StarsStatus.parse_starsStatus($0) } dict[-1261053863] = { return Api.payments.SuggestedStarRefBots.parse_suggestedStarRefBots($0) } - dict[-895289845] = { return Api.payments.UniqueStarGift.parse_uniqueStarGift($0) } + dict[1097619176] = { return Api.payments.UniqueStarGift.parse_uniqueStarGift($0) } + dict[1362093126] = { return Api.payments.UniqueStarGiftValueInfo.parse_uniqueStarGiftValueInfo($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[541839704] = { return Api.phone.ExportedGroupCallInvite.parse_exportedGroupCallInvite($0) } dict[-1636664659] = { return Api.phone.GroupCall.parse_groupCall($0) } @@ -1503,6 +1524,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[157948117] = { return Api.upload.File.parse_file($0) } dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) } dict[568808380] = { return Api.upload.WebFile.parse_webFile($0) } + dict[883094167] = { return Api.users.SavedMusic.parse_savedMusic($0) } + dict[-477656412] = { return Api.users.SavedMusic.parse_savedMusicNotModified($0) } dict[997004590] = { return Api.users.UserFull.parse_userFull($0) } dict[1658259128] = { return Api.users.Users.parse_users($0) } dict[828000628] = { return Api.users.Users.parse_usersSlice($0) } @@ -1689,6 +1712,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.ChatReactions: _1.serialize(buffer, boxed) + case let _1 as Api.ChatTheme: + _1.serialize(buffer, boxed) case let _1 as Api.CodeSettings: _1.serialize(buffer, boxed) case let _1 as Api.Config: @@ -1825,6 +1850,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.InputChatPhoto: _1.serialize(buffer, boxed) + case let _1 as Api.InputChatTheme: + _1.serialize(buffer, boxed) case let _1 as Api.InputChatlist: _1.serialize(buffer, boxed) case let _1 as Api.InputCheckPasswordSRP: @@ -2059,6 +2086,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.PrivacyRule: _1.serialize(buffer, boxed) + case let _1 as Api.ProfileTab: + _1.serialize(buffer, boxed) case let _1 as Api.PublicForward: _1.serialize(buffer, boxed) case let _1 as Api.QuickReply: @@ -2281,6 +2310,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.account.BusinessChatLinks: _1.serialize(buffer, boxed) + case let _1 as Api.account.ChatThemes: + _1.serialize(buffer, boxed) case let _1 as Api.account.ConnectedBots: _1.serialize(buffer, boxed) case let _1 as Api.account.ContentSettings: @@ -2303,6 +2334,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.account.ResolvedBusinessChatLinks: _1.serialize(buffer, boxed) + case let _1 as Api.account.SavedMusicIds: + _1.serialize(buffer, boxed) case let _1 as Api.account.SavedRingtone: _1.serialize(buffer, boxed) case let _1 as Api.account.SavedRingtones: @@ -2539,6 +2572,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.payments.BankCardData: _1.serialize(buffer, boxed) + case let _1 as Api.payments.CheckCanSendGiftResult: + _1.serialize(buffer, boxed) case let _1 as Api.payments.CheckedGiftCode: _1.serialize(buffer, boxed) case let _1 as Api.payments.ConnectedStarRefBots: @@ -2579,6 +2614,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.payments.UniqueStarGift: _1.serialize(buffer, boxed) + case let _1 as Api.payments.UniqueStarGiftValueInfo: + _1.serialize(buffer, boxed) case let _1 as Api.payments.ValidatedRequestedInfo: _1.serialize(buffer, boxed) case let _1 as Api.phone.ExportedGroupCallInvite: @@ -2653,6 +2690,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.upload.WebFile: _1.serialize(buffer, boxed) + case let _1 as Api.users.SavedMusic: + _1.serialize(buffer, boxed) case let _1 as Api.users.UserFull: _1.serialize(buffer, boxed) case let _1 as Api.users.Users: diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 1de673b91a..381dad6f6e 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -255,6 +255,7 @@ public extension Api { case inputInvoicePremiumGiftStars(flags: Int32, userId: Api.InputUser, months: Int32, message: Api.TextWithEntities?) case inputInvoiceSlug(slug: String) case inputInvoiceStarGift(flags: Int32, peer: Api.InputPeer, giftId: Int64, message: Api.TextWithEntities?) + case inputInvoiceStarGiftPrepaidUpgrade(peer: Api.InputPeer, hash: String) case inputInvoiceStarGiftResale(flags: Int32, slug: String, toId: Api.InputPeer) case inputInvoiceStarGiftTransfer(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) case inputInvoiceStarGiftUpgrade(flags: Int32, stargift: Api.InputSavedStarGift) @@ -313,6 +314,13 @@ public extension Api { serializeInt64(giftId, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)} break + case .inputInvoiceStarGiftPrepaidUpgrade(let peer, let hash): + if boxed { + buffer.appendInt32(-1710536520) + } + peer.serialize(buffer, true) + serializeString(hash, buffer: buffer, boxed: false) + break case .inputInvoiceStarGiftResale(let flags, let slug, let toId): if boxed { buffer.appendInt32(-1012968668) @@ -360,6 +368,8 @@ public extension Api { return ("inputInvoiceSlug", [("slug", slug as Any)]) case .inputInvoiceStarGift(let flags, let peer, let giftId, let message): return ("inputInvoiceStarGift", [("flags", flags as Any), ("peer", peer as Any), ("giftId", giftId as Any), ("message", message as Any)]) + case .inputInvoiceStarGiftPrepaidUpgrade(let peer, let hash): + return ("inputInvoiceStarGiftPrepaidUpgrade", [("peer", peer as Any), ("hash", hash as Any)]) case .inputInvoiceStarGiftResale(let flags, let slug, let toId): return ("inputInvoiceStarGiftResale", [("flags", flags as Any), ("slug", slug as Any), ("toId", toId as Any)]) case .inputInvoiceStarGiftTransfer(let stargift, let toId): @@ -491,6 +501,22 @@ public extension Api { return nil } } + public static func parse_inputInvoiceStarGiftPrepaidUpgrade(_ reader: BufferReader) -> InputInvoice? { + var _1: Api.InputPeer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputPeer + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InputInvoice.inputInvoiceStarGiftPrepaidUpgrade(peer: _1!, hash: _2!) + } + else { + return nil + } + } public static func parse_inputInvoiceStarGiftResale(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index d0c6d7ee81..f42d1da54f 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -123,7 +123,7 @@ public extension Api { case inputStorePaymentPremiumSubscription(flags: Int32) case inputStorePaymentStarsGift(userId: Api.InputUser, stars: Int64, currency: String, amount: Int64) case inputStorePaymentStarsGiveaway(flags: Int32, stars: Int64, boostPeer: Api.InputPeer, additionalPeers: [Api.InputPeer]?, countriesIso2: [String]?, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, users: Int32) - case inputStorePaymentStarsTopup(stars: Int64, currency: String, amount: Int64) + case inputStorePaymentStarsTopup(flags: Int32, stars: Int64, currency: String, amount: Int64, spendPurposePeer: Api.InputPeer?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -221,13 +221,15 @@ public extension Api { serializeInt64(amount, buffer: buffer, boxed: false) serializeInt32(users, buffer: buffer, boxed: false) break - case .inputStorePaymentStarsTopup(let stars, let currency, let amount): + case .inputStorePaymentStarsTopup(let flags, let stars, let currency, let amount, let spendPurposePeer): if boxed { - buffer.appendInt32(-572715178) + buffer.appendInt32(-106780981) } + serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(stars, buffer: buffer, boxed: false) serializeString(currency, buffer: buffer, boxed: false) serializeInt64(amount, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {spendPurposePeer!.serialize(buffer, true)} break } } @@ -248,8 +250,8 @@ public extension Api { return ("inputStorePaymentStarsGift", [("userId", userId as Any), ("stars", stars as Any), ("currency", currency as Any), ("amount", amount as Any)]) case .inputStorePaymentStarsGiveaway(let flags, let stars, let boostPeer, let additionalPeers, let countriesIso2, let prizeDescription, let randomId, let untilDate, let currency, let amount, let users): return ("inputStorePaymentStarsGiveaway", [("flags", flags as Any), ("stars", stars as Any), ("boostPeer", boostPeer as Any), ("additionalPeers", additionalPeers as Any), ("countriesIso2", countriesIso2 as Any), ("prizeDescription", prizeDescription as Any), ("randomId", randomId as Any), ("untilDate", untilDate as Any), ("currency", currency as Any), ("amount", amount as Any), ("users", users as Any)]) - case .inputStorePaymentStarsTopup(let stars, let currency, let amount): - return ("inputStorePaymentStarsTopup", [("stars", stars as Any), ("currency", currency as Any), ("amount", amount as Any)]) + case .inputStorePaymentStarsTopup(let flags, let stars, let currency, let amount, let spendPurposePeer): + return ("inputStorePaymentStarsTopup", [("flags", flags as Any), ("stars", stars as Any), ("currency", currency as Any), ("amount", amount as Any), ("spendPurposePeer", spendPurposePeer as Any)]) } } @@ -449,17 +451,25 @@ public extension Api { } } public static func parse_inputStorePaymentStarsTopup(_ reader: BufferReader) -> InputStorePaymentPurpose? { - var _1: Int64? - _1 = reader.readInt64() - var _2: String? - _2 = parseString(reader) - var _3: Int64? - _3 = reader.readInt64() + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + _3 = parseString(reader) + var _4: Int64? + _4 = reader.readInt64() + var _5: Api.InputPeer? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.InputPeer + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputStorePaymentPurpose.inputStorePaymentStarsTopup(stars: _1!, currency: _2!, amount: _3!) + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.InputStorePaymentPurpose.inputStorePaymentStarsTopup(flags: _1!, stars: _2!, currency: _3!, amount: _4!, spendPurposePeer: _5) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api15.swift b/submodules/TelegramApi/Sources/Api15.swift index 508eff99e8..ca9922c304 100644 --- a/submodules/TelegramApi/Sources/Api15.swift +++ b/submodules/TelegramApi/Sources/Api15.swift @@ -390,10 +390,10 @@ public extension Api { case messageActionScreenshotTaken case messageActionSecureValuesSent(types: [Api.SecureValueType]) case messageActionSecureValuesSentMe(values: [Api.SecureValue], credentials: Api.SecureCredentialsEncrypted) - case messageActionSetChatTheme(emoticon: String) + case messageActionSetChatTheme(theme: Api.ChatTheme) case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper) case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?) - case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?) + case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, prepaidUpgradeHash: String?, giftMsgId: Int32?) case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleAmount: Api.StarsAmount?, canTransferAt: Int32?, canResellAt: Int32?) case messageActionSuggestProfilePhoto(photo: Api.Photo) case messageActionSuggestedPostApproval(flags: Int32, rejectComment: String?, scheduleDate: Int32?, price: Api.StarsAmount?) @@ -762,11 +762,11 @@ public extension Api { } credentials.serialize(buffer, true) break - case .messageActionSetChatTheme(let emoticon): + case .messageActionSetChatTheme(let theme): if boxed { - buffer.appendInt32(-1434950843) + buffer.appendInt32(-1189364422) } - serializeString(emoticon, buffer: buffer, boxed: false) + theme.serialize(buffer, true) break case .messageActionSetChatWallPaper(let flags, let wallpaper): if boxed { @@ -783,9 +783,9 @@ public extension Api { serializeInt32(period, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt64(autoSettingFrom!, buffer: buffer, boxed: false)} break - case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId): + case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId): if boxed { - buffer.appendInt32(1192749220) + buffer.appendInt32(-229775366) } serializeInt32(flags, buffer: buffer, boxed: false) gift.serialize(buffer, true) @@ -796,6 +796,8 @@ public extension Api { if Int(flags) & Int(1 << 11) != 0 {fromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 12) != 0 {peer!.serialize(buffer, true)} if Int(flags) & Int(1 << 12) != 0 {serializeInt64(savedId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {serializeString(prepaidUpgradeHash!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(giftMsgId!, buffer: buffer, boxed: false)} break case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt): if boxed { @@ -985,14 +987,14 @@ public extension Api { return ("messageActionSecureValuesSent", [("types", types as Any)]) case .messageActionSecureValuesSentMe(let values, let credentials): return ("messageActionSecureValuesSentMe", [("values", values as Any), ("credentials", credentials as Any)]) - case .messageActionSetChatTheme(let emoticon): - return ("messageActionSetChatTheme", [("emoticon", emoticon as Any)]) + case .messageActionSetChatTheme(let theme): + return ("messageActionSetChatTheme", [("theme", theme as Any)]) case .messageActionSetChatWallPaper(let flags, let wallpaper): return ("messageActionSetChatWallPaper", [("flags", flags as Any), ("wallpaper", wallpaper as Any)]) case .messageActionSetMessagesTTL(let flags, let period, let autoSettingFrom): return ("messageActionSetMessagesTTL", [("flags", flags as Any), ("period", period as Any), ("autoSettingFrom", autoSettingFrom as Any)]) - case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId): - return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any)]) + case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars, let fromId, let peer, let savedId, let prepaidUpgradeHash, let giftMsgId): + return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any), ("giftMsgId", giftMsgId as Any)]) case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars, let fromId, let peer, let savedId, let resaleAmount, let canTransferAt, let canResellAt): return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleAmount", resaleAmount as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)]) case .messageActionSuggestProfilePhoto(let photo): @@ -1685,11 +1687,13 @@ public extension Api { } } public static func parse_messageActionSetChatTheme(_ reader: BufferReader) -> MessageAction? { - var _1: String? - _1 = parseString(reader) + var _1: Api.ChatTheme? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.ChatTheme + } let _c1 = _1 != nil if _c1 { - return Api.MessageAction.messageActionSetChatTheme(emoticon: _1!) + return Api.MessageAction.messageActionSetChatTheme(theme: _1!) } else { return nil @@ -1755,6 +1759,10 @@ public extension Api { } } var _9: Int64? if Int(_1!) & Int(1 << 12) != 0 {_9 = reader.readInt64() } + var _10: String? + if Int(_1!) & Int(1 << 14) != 0 {_10 = parseString(reader) } + var _11: Int32? + if Int(_1!) & Int(1 << 15) != 0 {_11 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil @@ -1764,8 +1772,10 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 12) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 12) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9) + let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 15) == 0) || _11 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { + return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9, prepaidUpgradeHash: _10, giftMsgId: _11) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api21.swift b/submodules/TelegramApi/Sources/Api21.swift index 74fa5e5c6f..9d4e90606d 100644 --- a/submodules/TelegramApi/Sources/Api21.swift +++ b/submodules/TelegramApi/Sources/Api21.swift @@ -214,6 +214,118 @@ public extension Api { } } +public extension Api { + enum ProfileTab: TypeConstructorDescription { + case profileTabFiles + case profileTabGifs + case profileTabGifts + case profileTabLinks + case profileTabMedia + case profileTabMusic + case profileTabPosts + case profileTabVoice + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .profileTabFiles: + if boxed { + buffer.appendInt32(-1422681088) + } + + break + case .profileTabGifs: + if boxed { + buffer.appendInt32(-1564412267) + } + + break + case .profileTabGifts: + if boxed { + buffer.appendInt32(1296815210) + } + + break + case .profileTabLinks: + if boxed { + buffer.appendInt32(-748329831) + } + + break + case .profileTabMedia: + if boxed { + buffer.appendInt32(1925597525) + } + + break + case .profileTabMusic: + if boxed { + buffer.appendInt32(-1624780178) + } + + break + case .profileTabPosts: + if boxed { + buffer.appendInt32(-1181952362) + } + + break + case .profileTabVoice: + if boxed { + buffer.appendInt32(-461960914) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .profileTabFiles: + return ("profileTabFiles", []) + case .profileTabGifs: + return ("profileTabGifs", []) + case .profileTabGifts: + return ("profileTabGifts", []) + case .profileTabLinks: + return ("profileTabLinks", []) + case .profileTabMedia: + return ("profileTabMedia", []) + case .profileTabMusic: + return ("profileTabMusic", []) + case .profileTabPosts: + return ("profileTabPosts", []) + case .profileTabVoice: + return ("profileTabVoice", []) + } + } + + public static func parse_profileTabFiles(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabFiles + } + public static func parse_profileTabGifs(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabGifs + } + public static func parse_profileTabGifts(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabGifts + } + public static func parse_profileTabLinks(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabLinks + } + public static func parse_profileTabMedia(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabMedia + } + public static func parse_profileTabMusic(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabMusic + } + public static func parse_profileTabPosts(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabPosts + } + public static func parse_profileTabVoice(_ reader: BufferReader) -> ProfileTab? { + return Api.ProfileTab.profileTabVoice + } + + } +} public extension Api { indirect enum PublicForward: TypeConstructorDescription { case publicForwardMessage(message: Api.Message) @@ -598,299 +710,3 @@ public extension Api { } } -public extension Api { - enum ReceivedNotifyMessage: TypeConstructorDescription { - case receivedNotifyMessage(id: Int32, flags: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .receivedNotifyMessage(let id, let flags): - if boxed { - buffer.appendInt32(-1551583367) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(flags, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .receivedNotifyMessage(let id, let flags): - return ("receivedNotifyMessage", [("id", id as Any), ("flags", flags as Any)]) - } - } - - public static func parse_receivedNotifyMessage(_ reader: BufferReader) -> ReceivedNotifyMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ReceivedNotifyMessage.receivedNotifyMessage(id: _1!, flags: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - indirect enum RecentMeUrl: TypeConstructorDescription { - case recentMeUrlChat(url: String, chatId: Int64) - case recentMeUrlChatInvite(url: String, chatInvite: Api.ChatInvite) - case recentMeUrlStickerSet(url: String, set: Api.StickerSetCovered) - case recentMeUrlUnknown(url: String) - case recentMeUrlUser(url: String, userId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .recentMeUrlChat(let url, let chatId): - if boxed { - buffer.appendInt32(-1294306862) - } - serializeString(url, buffer: buffer, boxed: false) - serializeInt64(chatId, buffer: buffer, boxed: false) - break - case .recentMeUrlChatInvite(let url, let chatInvite): - if boxed { - buffer.appendInt32(-347535331) - } - serializeString(url, buffer: buffer, boxed: false) - chatInvite.serialize(buffer, true) - break - case .recentMeUrlStickerSet(let url, let set): - if boxed { - buffer.appendInt32(-1140172836) - } - serializeString(url, buffer: buffer, boxed: false) - set.serialize(buffer, true) - break - case .recentMeUrlUnknown(let url): - if boxed { - buffer.appendInt32(1189204285) - } - serializeString(url, buffer: buffer, boxed: false) - break - case .recentMeUrlUser(let url, let userId): - if boxed { - buffer.appendInt32(-1188296222) - } - serializeString(url, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .recentMeUrlChat(let url, let chatId): - return ("recentMeUrlChat", [("url", url as Any), ("chatId", chatId as Any)]) - case .recentMeUrlChatInvite(let url, let chatInvite): - return ("recentMeUrlChatInvite", [("url", url as Any), ("chatInvite", chatInvite as Any)]) - case .recentMeUrlStickerSet(let url, let set): - return ("recentMeUrlStickerSet", [("url", url as Any), ("set", set as Any)]) - case .recentMeUrlUnknown(let url): - return ("recentMeUrlUnknown", [("url", url as Any)]) - case .recentMeUrlUser(let url, let userId): - return ("recentMeUrlUser", [("url", url as Any), ("userId", userId as Any)]) - } - } - - public static func parse_recentMeUrlChat(_ reader: BufferReader) -> RecentMeUrl? { - var _1: String? - _1 = parseString(reader) - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlChat(url: _1!, chatId: _2!) - } - else { - return nil - } - } - public static func parse_recentMeUrlChatInvite(_ reader: BufferReader) -> RecentMeUrl? { - var _1: String? - _1 = parseString(reader) - var _2: Api.ChatInvite? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.ChatInvite - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlChatInvite(url: _1!, chatInvite: _2!) - } - else { - return nil - } - } - public static func parse_recentMeUrlStickerSet(_ reader: BufferReader) -> RecentMeUrl? { - var _1: String? - _1 = parseString(reader) - var _2: Api.StickerSetCovered? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StickerSetCovered - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlStickerSet(url: _1!, set: _2!) - } - else { - return nil - } - } - public static func parse_recentMeUrlUnknown(_ reader: BufferReader) -> RecentMeUrl? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.RecentMeUrl.recentMeUrlUnknown(url: _1!) - } - else { - return nil - } - } - public static func parse_recentMeUrlUser(_ reader: BufferReader) -> RecentMeUrl? { - var _1: String? - _1 = parseString(reader) - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlUser(url: _1!, userId: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum ReplyMarkup: TypeConstructorDescription { - case replyInlineMarkup(rows: [Api.KeyboardButtonRow]) - case replyKeyboardForceReply(flags: Int32, placeholder: String?) - case replyKeyboardHide(flags: Int32) - case replyKeyboardMarkup(flags: Int32, rows: [Api.KeyboardButtonRow], placeholder: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .replyInlineMarkup(let rows): - if boxed { - buffer.appendInt32(1218642516) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(rows.count)) - for item in rows { - item.serialize(buffer, true) - } - break - case .replyKeyboardForceReply(let flags, let placeholder): - if boxed { - buffer.appendInt32(-2035021048) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeString(placeholder!, buffer: buffer, boxed: false)} - break - case .replyKeyboardHide(let flags): - if boxed { - buffer.appendInt32(-1606526075) - } - serializeInt32(flags, buffer: buffer, boxed: false) - break - case .replyKeyboardMarkup(let flags, let rows, let placeholder): - if boxed { - buffer.appendInt32(-2049074735) - } - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(rows.count)) - for item in rows { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 3) != 0 {serializeString(placeholder!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .replyInlineMarkup(let rows): - return ("replyInlineMarkup", [("rows", rows as Any)]) - case .replyKeyboardForceReply(let flags, let placeholder): - return ("replyKeyboardForceReply", [("flags", flags as Any), ("placeholder", placeholder as Any)]) - case .replyKeyboardHide(let flags): - return ("replyKeyboardHide", [("flags", flags as Any)]) - case .replyKeyboardMarkup(let flags, let rows, let placeholder): - return ("replyKeyboardMarkup", [("flags", flags as Any), ("rows", rows as Any), ("placeholder", placeholder as Any)]) - } - } - - public static func parse_replyInlineMarkup(_ reader: BufferReader) -> ReplyMarkup? { - var _1: [Api.KeyboardButtonRow]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButtonRow.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.ReplyMarkup.replyInlineMarkup(rows: _1!) - } - else { - return nil - } - } - public static func parse_replyKeyboardForceReply(_ reader: BufferReader) -> ReplyMarkup? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 3) != 0 {_2 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil - if _c1 && _c2 { - return Api.ReplyMarkup.replyKeyboardForceReply(flags: _1!, placeholder: _2) - } - else { - return nil - } - } - public static func parse_replyKeyboardHide(_ reader: BufferReader) -> ReplyMarkup? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.ReplyMarkup.replyKeyboardHide(flags: _1!) - } - else { - return nil - } - } - public static func parse_replyKeyboardMarkup(_ reader: BufferReader) -> ReplyMarkup? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.KeyboardButtonRow]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButtonRow.self) - } - var _3: String? - if Int(_1!) & Int(1 << 3) != 0 {_3 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.ReplyMarkup.replyKeyboardMarkup(flags: _1!, rows: _2!, placeholder: _3) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 11d681d562..4d217f68c2 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -1,3 +1,299 @@ +public extension Api { + enum ReceivedNotifyMessage: TypeConstructorDescription { + case receivedNotifyMessage(id: Int32, flags: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .receivedNotifyMessage(let id, let flags): + if boxed { + buffer.appendInt32(-1551583367) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(flags, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .receivedNotifyMessage(let id, let flags): + return ("receivedNotifyMessage", [("id", id as Any), ("flags", flags as Any)]) + } + } + + public static func parse_receivedNotifyMessage(_ reader: BufferReader) -> ReceivedNotifyMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.ReceivedNotifyMessage.receivedNotifyMessage(id: _1!, flags: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum RecentMeUrl: TypeConstructorDescription { + case recentMeUrlChat(url: String, chatId: Int64) + case recentMeUrlChatInvite(url: String, chatInvite: Api.ChatInvite) + case recentMeUrlStickerSet(url: String, set: Api.StickerSetCovered) + case recentMeUrlUnknown(url: String) + case recentMeUrlUser(url: String, userId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .recentMeUrlChat(let url, let chatId): + if boxed { + buffer.appendInt32(-1294306862) + } + serializeString(url, buffer: buffer, boxed: false) + serializeInt64(chatId, buffer: buffer, boxed: false) + break + case .recentMeUrlChatInvite(let url, let chatInvite): + if boxed { + buffer.appendInt32(-347535331) + } + serializeString(url, buffer: buffer, boxed: false) + chatInvite.serialize(buffer, true) + break + case .recentMeUrlStickerSet(let url, let set): + if boxed { + buffer.appendInt32(-1140172836) + } + serializeString(url, buffer: buffer, boxed: false) + set.serialize(buffer, true) + break + case .recentMeUrlUnknown(let url): + if boxed { + buffer.appendInt32(1189204285) + } + serializeString(url, buffer: buffer, boxed: false) + break + case .recentMeUrlUser(let url, let userId): + if boxed { + buffer.appendInt32(-1188296222) + } + serializeString(url, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .recentMeUrlChat(let url, let chatId): + return ("recentMeUrlChat", [("url", url as Any), ("chatId", chatId as Any)]) + case .recentMeUrlChatInvite(let url, let chatInvite): + return ("recentMeUrlChatInvite", [("url", url as Any), ("chatInvite", chatInvite as Any)]) + case .recentMeUrlStickerSet(let url, let set): + return ("recentMeUrlStickerSet", [("url", url as Any), ("set", set as Any)]) + case .recentMeUrlUnknown(let url): + return ("recentMeUrlUnknown", [("url", url as Any)]) + case .recentMeUrlUser(let url, let userId): + return ("recentMeUrlUser", [("url", url as Any), ("userId", userId as Any)]) + } + } + + public static func parse_recentMeUrlChat(_ reader: BufferReader) -> RecentMeUrl? { + var _1: String? + _1 = parseString(reader) + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RecentMeUrl.recentMeUrlChat(url: _1!, chatId: _2!) + } + else { + return nil + } + } + public static func parse_recentMeUrlChatInvite(_ reader: BufferReader) -> RecentMeUrl? { + var _1: String? + _1 = parseString(reader) + var _2: Api.ChatInvite? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.ChatInvite + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RecentMeUrl.recentMeUrlChatInvite(url: _1!, chatInvite: _2!) + } + else { + return nil + } + } + public static func parse_recentMeUrlStickerSet(_ reader: BufferReader) -> RecentMeUrl? { + var _1: String? + _1 = parseString(reader) + var _2: Api.StickerSetCovered? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StickerSetCovered + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RecentMeUrl.recentMeUrlStickerSet(url: _1!, set: _2!) + } + else { + return nil + } + } + public static func parse_recentMeUrlUnknown(_ reader: BufferReader) -> RecentMeUrl? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.RecentMeUrl.recentMeUrlUnknown(url: _1!) + } + else { + return nil + } + } + public static func parse_recentMeUrlUser(_ reader: BufferReader) -> RecentMeUrl? { + var _1: String? + _1 = parseString(reader) + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RecentMeUrl.recentMeUrlUser(url: _1!, userId: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum ReplyMarkup: TypeConstructorDescription { + case replyInlineMarkup(rows: [Api.KeyboardButtonRow]) + case replyKeyboardForceReply(flags: Int32, placeholder: String?) + case replyKeyboardHide(flags: Int32) + case replyKeyboardMarkup(flags: Int32, rows: [Api.KeyboardButtonRow], placeholder: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .replyInlineMarkup(let rows): + if boxed { + buffer.appendInt32(1218642516) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(rows.count)) + for item in rows { + item.serialize(buffer, true) + } + break + case .replyKeyboardForceReply(let flags, let placeholder): + if boxed { + buffer.appendInt32(-2035021048) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeString(placeholder!, buffer: buffer, boxed: false)} + break + case .replyKeyboardHide(let flags): + if boxed { + buffer.appendInt32(-1606526075) + } + serializeInt32(flags, buffer: buffer, boxed: false) + break + case .replyKeyboardMarkup(let flags, let rows, let placeholder): + if boxed { + buffer.appendInt32(-2049074735) + } + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(rows.count)) + for item in rows { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 3) != 0 {serializeString(placeholder!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .replyInlineMarkup(let rows): + return ("replyInlineMarkup", [("rows", rows as Any)]) + case .replyKeyboardForceReply(let flags, let placeholder): + return ("replyKeyboardForceReply", [("flags", flags as Any), ("placeholder", placeholder as Any)]) + case .replyKeyboardHide(let flags): + return ("replyKeyboardHide", [("flags", flags as Any)]) + case .replyKeyboardMarkup(let flags, let rows, let placeholder): + return ("replyKeyboardMarkup", [("flags", flags as Any), ("rows", rows as Any), ("placeholder", placeholder as Any)]) + } + } + + public static func parse_replyInlineMarkup(_ reader: BufferReader) -> ReplyMarkup? { + var _1: [Api.KeyboardButtonRow]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButtonRow.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.ReplyMarkup.replyInlineMarkup(rows: _1!) + } + else { + return nil + } + } + public static func parse_replyKeyboardForceReply(_ reader: BufferReader) -> ReplyMarkup? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + if Int(_1!) & Int(1 << 3) != 0 {_2 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil + if _c1 && _c2 { + return Api.ReplyMarkup.replyKeyboardForceReply(flags: _1!, placeholder: _2) + } + else { + return nil + } + } + public static func parse_replyKeyboardHide(_ reader: BufferReader) -> ReplyMarkup? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.ReplyMarkup.replyKeyboardHide(flags: _1!) + } + else { + return nil + } + } + public static func parse_replyKeyboardMarkup(_ reader: BufferReader) -> ReplyMarkup? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.KeyboardButtonRow]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButtonRow.self) + } + var _3: String? + if Int(_1!) & Int(1 << 3) != 0 {_3 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.ReplyMarkup.replyKeyboardMarkup(flags: _1!, rows: _2!, placeholder: _3) + } + else { + return nil + } + } + + } +} public extension Api { enum ReportReason: TypeConstructorDescription { case inputReportReasonChildAbuse @@ -536,433 +832,3 @@ public extension Api { } } -public extension Api { - enum RestrictionReason: TypeConstructorDescription { - case restrictionReason(platform: String, reason: String, text: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .restrictionReason(let platform, let reason, let text): - if boxed { - buffer.appendInt32(-797791052) - } - serializeString(platform, buffer: buffer, boxed: false) - serializeString(reason, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .restrictionReason(let platform, let reason, let text): - return ("restrictionReason", [("platform", platform as Any), ("reason", reason as Any), ("text", text as Any)]) - } - } - - public static func parse_restrictionReason(_ reader: BufferReader) -> RestrictionReason? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - indirect enum RichText: TypeConstructorDescription { - case textAnchor(text: Api.RichText, name: String) - case textBold(text: Api.RichText) - case textConcat(texts: [Api.RichText]) - case textEmail(text: Api.RichText, email: String) - case textEmpty - case textFixed(text: Api.RichText) - case textImage(documentId: Int64, w: Int32, h: Int32) - case textItalic(text: Api.RichText) - case textMarked(text: Api.RichText) - case textPhone(text: Api.RichText, phone: String) - case textPlain(text: String) - case textStrike(text: Api.RichText) - case textSubscript(text: Api.RichText) - case textSuperscript(text: Api.RichText) - case textUnderline(text: Api.RichText) - case textUrl(text: Api.RichText, url: String, webpageId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .textAnchor(let text, let name): - if boxed { - buffer.appendInt32(894777186) - } - text.serialize(buffer, true) - serializeString(name, buffer: buffer, boxed: false) - break - case .textBold(let text): - if boxed { - buffer.appendInt32(1730456516) - } - text.serialize(buffer, true) - break - case .textConcat(let texts): - if boxed { - buffer.appendInt32(2120376535) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(texts.count)) - for item in texts { - item.serialize(buffer, true) - } - break - case .textEmail(let text, let email): - if boxed { - buffer.appendInt32(-564523562) - } - text.serialize(buffer, true) - serializeString(email, buffer: buffer, boxed: false) - break - case .textEmpty: - if boxed { - buffer.appendInt32(-599948721) - } - - break - case .textFixed(let text): - if boxed { - buffer.appendInt32(1816074681) - } - text.serialize(buffer, true) - break - case .textImage(let documentId, let w, let h): - if boxed { - buffer.appendInt32(136105807) - } - serializeInt64(documentId, buffer: buffer, boxed: false) - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - break - case .textItalic(let text): - if boxed { - buffer.appendInt32(-653089380) - } - text.serialize(buffer, true) - break - case .textMarked(let text): - if boxed { - buffer.appendInt32(55281185) - } - text.serialize(buffer, true) - break - case .textPhone(let text, let phone): - if boxed { - buffer.appendInt32(483104362) - } - text.serialize(buffer, true) - serializeString(phone, buffer: buffer, boxed: false) - break - case .textPlain(let text): - if boxed { - buffer.appendInt32(1950782688) - } - serializeString(text, buffer: buffer, boxed: false) - break - case .textStrike(let text): - if boxed { - buffer.appendInt32(-1678197867) - } - text.serialize(buffer, true) - break - case .textSubscript(let text): - if boxed { - buffer.appendInt32(-311786236) - } - text.serialize(buffer, true) - break - case .textSuperscript(let text): - if boxed { - buffer.appendInt32(-939827711) - } - text.serialize(buffer, true) - break - case .textUnderline(let text): - if boxed { - buffer.appendInt32(-1054465340) - } - text.serialize(buffer, true) - break - case .textUrl(let text, let url, let webpageId): - if boxed { - buffer.appendInt32(1009288385) - } - text.serialize(buffer, true) - serializeString(url, buffer: buffer, boxed: false) - serializeInt64(webpageId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .textAnchor(let text, let name): - return ("textAnchor", [("text", text as Any), ("name", name as Any)]) - case .textBold(let text): - return ("textBold", [("text", text as Any)]) - case .textConcat(let texts): - return ("textConcat", [("texts", texts as Any)]) - case .textEmail(let text, let email): - return ("textEmail", [("text", text as Any), ("email", email as Any)]) - case .textEmpty: - return ("textEmpty", []) - case .textFixed(let text): - return ("textFixed", [("text", text as Any)]) - case .textImage(let documentId, let w, let h): - return ("textImage", [("documentId", documentId as Any), ("w", w as Any), ("h", h as Any)]) - case .textItalic(let text): - return ("textItalic", [("text", text as Any)]) - case .textMarked(let text): - return ("textMarked", [("text", text as Any)]) - case .textPhone(let text, let phone): - return ("textPhone", [("text", text as Any), ("phone", phone as Any)]) - case .textPlain(let text): - return ("textPlain", [("text", text as Any)]) - case .textStrike(let text): - return ("textStrike", [("text", text as Any)]) - case .textSubscript(let text): - return ("textSubscript", [("text", text as Any)]) - case .textSuperscript(let text): - return ("textSuperscript", [("text", text as Any)]) - case .textUnderline(let text): - return ("textUnderline", [("text", text as Any)]) - case .textUrl(let text, let url, let webpageId): - return ("textUrl", [("text", text as Any), ("url", url as Any), ("webpageId", webpageId as Any)]) - } - } - - public static func parse_textAnchor(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textAnchor(text: _1!, name: _2!) - } - else { - return nil - } - } - public static func parse_textBold(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textBold(text: _1!) - } - else { - return nil - } - } - public static func parse_textConcat(_ reader: BufferReader) -> RichText? { - var _1: [Api.RichText]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RichText.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textConcat(texts: _1!) - } - else { - return nil - } - } - public static func parse_textEmail(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textEmail(text: _1!, email: _2!) - } - else { - return nil - } - } - public static func parse_textEmpty(_ reader: BufferReader) -> RichText? { - return Api.RichText.textEmpty - } - public static func parse_textFixed(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textFixed(text: _1!) - } - else { - return nil - } - } - public static func parse_textImage(_ reader: BufferReader) -> RichText? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!) - } - else { - return nil - } - } - public static func parse_textItalic(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textItalic(text: _1!) - } - else { - return nil - } - } - public static func parse_textMarked(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textMarked(text: _1!) - } - else { - return nil - } - } - public static func parse_textPhone(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textPhone(text: _1!, phone: _2!) - } - else { - return nil - } - } - public static func parse_textPlain(_ reader: BufferReader) -> RichText? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textPlain(text: _1!) - } - else { - return nil - } - } - public static func parse_textStrike(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textStrike(text: _1!) - } - else { - return nil - } - } - public static func parse_textSubscript(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textSubscript(text: _1!) - } - else { - return nil - } - } - public static func parse_textSuperscript(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textSuperscript(text: _1!) - } - else { - return nil - } - } - public static func parse_textUnderline(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - let _c1 = _1 != nil - if _c1 { - return Api.RichText.textUnderline(text: _1!) - } - else { - return nil - } - } - public static func parse_textUrl(_ reader: BufferReader) -> RichText? { - var _1: Api.RichText? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.RichText - } - var _2: String? - _2 = parseString(reader) - var _3: Int64? - _3 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _3!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index faab39ce1d..7a79ed07e0 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -1,3 +1,433 @@ +public extension Api { + enum RestrictionReason: TypeConstructorDescription { + case restrictionReason(platform: String, reason: String, text: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .restrictionReason(let platform, let reason, let text): + if boxed { + buffer.appendInt32(-797791052) + } + serializeString(platform, buffer: buffer, boxed: false) + serializeString(reason, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .restrictionReason(let platform, let reason, let text): + return ("restrictionReason", [("platform", platform as Any), ("reason", reason as Any), ("text", text as Any)]) + } + } + + public static func parse_restrictionReason(_ reader: BufferReader) -> RestrictionReason? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum RichText: TypeConstructorDescription { + case textAnchor(text: Api.RichText, name: String) + case textBold(text: Api.RichText) + case textConcat(texts: [Api.RichText]) + case textEmail(text: Api.RichText, email: String) + case textEmpty + case textFixed(text: Api.RichText) + case textImage(documentId: Int64, w: Int32, h: Int32) + case textItalic(text: Api.RichText) + case textMarked(text: Api.RichText) + case textPhone(text: Api.RichText, phone: String) + case textPlain(text: String) + case textStrike(text: Api.RichText) + case textSubscript(text: Api.RichText) + case textSuperscript(text: Api.RichText) + case textUnderline(text: Api.RichText) + case textUrl(text: Api.RichText, url: String, webpageId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .textAnchor(let text, let name): + if boxed { + buffer.appendInt32(894777186) + } + text.serialize(buffer, true) + serializeString(name, buffer: buffer, boxed: false) + break + case .textBold(let text): + if boxed { + buffer.appendInt32(1730456516) + } + text.serialize(buffer, true) + break + case .textConcat(let texts): + if boxed { + buffer.appendInt32(2120376535) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(texts.count)) + for item in texts { + item.serialize(buffer, true) + } + break + case .textEmail(let text, let email): + if boxed { + buffer.appendInt32(-564523562) + } + text.serialize(buffer, true) + serializeString(email, buffer: buffer, boxed: false) + break + case .textEmpty: + if boxed { + buffer.appendInt32(-599948721) + } + + break + case .textFixed(let text): + if boxed { + buffer.appendInt32(1816074681) + } + text.serialize(buffer, true) + break + case .textImage(let documentId, let w, let h): + if boxed { + buffer.appendInt32(136105807) + } + serializeInt64(documentId, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break + case .textItalic(let text): + if boxed { + buffer.appendInt32(-653089380) + } + text.serialize(buffer, true) + break + case .textMarked(let text): + if boxed { + buffer.appendInt32(55281185) + } + text.serialize(buffer, true) + break + case .textPhone(let text, let phone): + if boxed { + buffer.appendInt32(483104362) + } + text.serialize(buffer, true) + serializeString(phone, buffer: buffer, boxed: false) + break + case .textPlain(let text): + if boxed { + buffer.appendInt32(1950782688) + } + serializeString(text, buffer: buffer, boxed: false) + break + case .textStrike(let text): + if boxed { + buffer.appendInt32(-1678197867) + } + text.serialize(buffer, true) + break + case .textSubscript(let text): + if boxed { + buffer.appendInt32(-311786236) + } + text.serialize(buffer, true) + break + case .textSuperscript(let text): + if boxed { + buffer.appendInt32(-939827711) + } + text.serialize(buffer, true) + break + case .textUnderline(let text): + if boxed { + buffer.appendInt32(-1054465340) + } + text.serialize(buffer, true) + break + case .textUrl(let text, let url, let webpageId): + if boxed { + buffer.appendInt32(1009288385) + } + text.serialize(buffer, true) + serializeString(url, buffer: buffer, boxed: false) + serializeInt64(webpageId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .textAnchor(let text, let name): + return ("textAnchor", [("text", text as Any), ("name", name as Any)]) + case .textBold(let text): + return ("textBold", [("text", text as Any)]) + case .textConcat(let texts): + return ("textConcat", [("texts", texts as Any)]) + case .textEmail(let text, let email): + return ("textEmail", [("text", text as Any), ("email", email as Any)]) + case .textEmpty: + return ("textEmpty", []) + case .textFixed(let text): + return ("textFixed", [("text", text as Any)]) + case .textImage(let documentId, let w, let h): + return ("textImage", [("documentId", documentId as Any), ("w", w as Any), ("h", h as Any)]) + case .textItalic(let text): + return ("textItalic", [("text", text as Any)]) + case .textMarked(let text): + return ("textMarked", [("text", text as Any)]) + case .textPhone(let text, let phone): + return ("textPhone", [("text", text as Any), ("phone", phone as Any)]) + case .textPlain(let text): + return ("textPlain", [("text", text as Any)]) + case .textStrike(let text): + return ("textStrike", [("text", text as Any)]) + case .textSubscript(let text): + return ("textSubscript", [("text", text as Any)]) + case .textSuperscript(let text): + return ("textSuperscript", [("text", text as Any)]) + case .textUnderline(let text): + return ("textUnderline", [("text", text as Any)]) + case .textUrl(let text, let url, let webpageId): + return ("textUrl", [("text", text as Any), ("url", url as Any), ("webpageId", webpageId as Any)]) + } + } + + public static func parse_textAnchor(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RichText.textAnchor(text: _1!, name: _2!) + } + else { + return nil + } + } + public static func parse_textBold(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textBold(text: _1!) + } + else { + return nil + } + } + public static func parse_textConcat(_ reader: BufferReader) -> RichText? { + var _1: [Api.RichText]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RichText.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textConcat(texts: _1!) + } + else { + return nil + } + } + public static func parse_textEmail(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RichText.textEmail(text: _1!, email: _2!) + } + else { + return nil + } + } + public static func parse_textEmpty(_ reader: BufferReader) -> RichText? { + return Api.RichText.textEmpty + } + public static func parse_textFixed(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textFixed(text: _1!) + } + else { + return nil + } + } + public static func parse_textImage(_ reader: BufferReader) -> RichText? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!) + } + else { + return nil + } + } + public static func parse_textItalic(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textItalic(text: _1!) + } + else { + return nil + } + } + public static func parse_textMarked(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textMarked(text: _1!) + } + else { + return nil + } + } + public static func parse_textPhone(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RichText.textPhone(text: _1!, phone: _2!) + } + else { + return nil + } + } + public static func parse_textPlain(_ reader: BufferReader) -> RichText? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textPlain(text: _1!) + } + else { + return nil + } + } + public static func parse_textStrike(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textStrike(text: _1!) + } + else { + return nil + } + } + public static func parse_textSubscript(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textSubscript(text: _1!) + } + else { + return nil + } + } + public static func parse_textSuperscript(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textSuperscript(text: _1!) + } + else { + return nil + } + } + public static func parse_textUnderline(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textUnderline(text: _1!) + } + else { + return nil + } + } + public static func parse_textUrl(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: String? + _2 = parseString(reader) + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum SavedContact: TypeConstructorDescription { case savedPhoneContact(phone: String, firstName: String, lastName: String, date: Int32) @@ -196,13 +626,13 @@ public extension Api { } public extension Api { enum SavedStarGift: TypeConstructorDescription { - case savedStarGift(flags: Int32, fromId: Api.Peer?, date: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, msgId: Int32?, savedId: Int64?, convertStars: Int64?, upgradeStars: Int64?, canExportAt: Int32?, transferStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?, collectionId: [Int32]?) + case savedStarGift(flags: Int32, fromId: Api.Peer?, date: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, msgId: Int32?, savedId: Int64?, convertStars: Int64?, upgradeStars: Int64?, canExportAt: Int32?, transferStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?, collectionId: [Int32]?, prepaidUpgradeHash: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId): + case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash): if boxed { - buffer.appendInt32(514213599) + buffer.appendInt32(430552434) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {fromId!.serialize(buffer, true)} @@ -222,14 +652,15 @@ public extension Api { for item in collectionId! { serializeInt32(item, buffer: buffer, boxed: false) }} + if Int(flags) & Int(1 << 16) != 0 {serializeString(prepaidUpgradeHash!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId): - return ("savedStarGift", [("flags", flags as Any), ("fromId", fromId as Any), ("date", date as Any), ("gift", gift as Any), ("message", message as Any), ("msgId", msgId as Any), ("savedId", savedId as Any), ("convertStars", convertStars as Any), ("upgradeStars", upgradeStars as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any), ("collectionId", collectionId as Any)]) + case .savedStarGift(let flags, let fromId, let date, let gift, let message, let msgId, let savedId, let convertStars, let upgradeStars, let canExportAt, let transferStars, let canTransferAt, let canResellAt, let collectionId, let prepaidUpgradeHash): + return ("savedStarGift", [("flags", flags as Any), ("fromId", fromId as Any), ("date", date as Any), ("gift", gift as Any), ("message", message as Any), ("msgId", msgId as Any), ("savedId", savedId as Any), ("convertStars", convertStars as Any), ("upgradeStars", upgradeStars as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any), ("collectionId", collectionId as Any), ("prepaidUpgradeHash", prepaidUpgradeHash as Any)]) } } @@ -270,6 +701,8 @@ public extension Api { if Int(_1!) & Int(1 << 15) != 0 {if let _ = reader.readInt32() { _14 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } } + var _15: String? + if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = _3 != nil @@ -284,8 +717,9 @@ public extension Api { let _c12 = (Int(_1!) & Int(1 << 13) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 14) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14) + let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14, prepaidUpgradeHash: _15) } else { return nil @@ -666,785 +1100,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 - } - } - - } -} -public extension Api { - enum SecureRequiredType: TypeConstructorDescription { - case secureRequiredType(flags: Int32, type: Api.SecureValueType) - case secureRequiredTypeOneOf(types: [Api.SecureRequiredType]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureRequiredType(let flags, let type): - if boxed { - buffer.appendInt32(-2103600678) - } - serializeInt32(flags, buffer: buffer, boxed: false) - type.serialize(buffer, true) - break - case .secureRequiredTypeOneOf(let types): - if boxed { - buffer.appendInt32(41187252) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(types.count)) - for item in types { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureRequiredType(let flags, let type): - return ("secureRequiredType", [("flags", flags as Any), ("type", type as Any)]) - case .secureRequiredTypeOneOf(let types): - return ("secureRequiredTypeOneOf", [("types", types as Any)]) - } - } - - public static func parse_secureRequiredType(_ reader: BufferReader) -> SecureRequiredType? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.SecureValueType? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SecureRequiredType.secureRequiredType(flags: _1!, type: _2!) - } - else { - return nil - } - } - public static func parse_secureRequiredTypeOneOf(_ reader: BufferReader) -> SecureRequiredType? { - var _1: [Api.SecureRequiredType]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.SecureRequiredType.secureRequiredTypeOneOf(types: _1!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SecureSecretSettings: TypeConstructorDescription { - case secureSecretSettings(secureAlgo: Api.SecurePasswordKdfAlgo, secureSecret: Buffer, secureSecretId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureSecretSettings(let secureAlgo, let secureSecret, let secureSecretId): - if boxed { - buffer.appendInt32(354925740) - } - secureAlgo.serialize(buffer, true) - serializeBytes(secureSecret, buffer: buffer, boxed: false) - serializeInt64(secureSecretId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureSecretSettings(let secureAlgo, let secureSecret, let secureSecretId): - return ("secureSecretSettings", [("secureAlgo", secureAlgo as Any), ("secureSecret", secureSecret as Any), ("secureSecretId", secureSecretId as Any)]) - } - } - - public static func parse_secureSecretSettings(_ reader: BufferReader) -> SecureSecretSettings? { - var _1: Api.SecurePasswordKdfAlgo? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecurePasswordKdfAlgo - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Int64? - _3 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureSecretSettings.secureSecretSettings(secureAlgo: _1!, secureSecret: _2!, secureSecretId: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SecureValue: TypeConstructorDescription { - case secureValue(flags: Int32, type: Api.SecureValueType, data: Api.SecureData?, frontSide: Api.SecureFile?, reverseSide: Api.SecureFile?, selfie: Api.SecureFile?, translation: [Api.SecureFile]?, files: [Api.SecureFile]?, plainData: Api.SecurePlainData?, hash: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData, let hash): - if boxed { - buffer.appendInt32(411017418) - } - serializeInt32(flags, buffer: buffer, boxed: false) - type.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {data!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {frontSide!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {reverseSide!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {selfie!.serialize(buffer, true)} - if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(translation!.count)) - for item in translation! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(files!.count)) - for item in files! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 5) != 0 {plainData!.serialize(buffer, true)} - serializeBytes(hash, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData, let hash): - return ("secureValue", [("flags", flags as Any), ("type", type as Any), ("data", data as Any), ("frontSide", frontSide as Any), ("reverseSide", reverseSide as Any), ("selfie", selfie as Any), ("translation", translation as Any), ("files", files as Any), ("plainData", plainData as Any), ("hash", hash as Any)]) - } - } - - public static func parse_secureValue(_ reader: BufferReader) -> SecureValue? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.SecureValueType? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _3: Api.SecureData? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.SecureData - } } - var _4: Api.SecureFile? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.SecureFile - } } - var _5: Api.SecureFile? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.SecureFile - } } - var _6: Api.SecureFile? - if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.SecureFile - } } - var _7: [Api.SecureFile]? - if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureFile.self) - } } - var _8: [Api.SecureFile]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureFile.self) - } } - var _9: Api.SecurePlainData? - if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.SecurePlainData - } } - var _10: Buffer? - _10 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil - let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.SecureValue.secureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9, hash: _10!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SecureValueError: TypeConstructorDescription { - case secureValueError(type: Api.SecureValueType, hash: Buffer, text: String) - case secureValueErrorData(type: Api.SecureValueType, dataHash: Buffer, field: String, text: String) - case secureValueErrorFile(type: Api.SecureValueType, fileHash: Buffer, text: String) - case secureValueErrorFiles(type: Api.SecureValueType, fileHash: [Buffer], text: String) - case secureValueErrorFrontSide(type: Api.SecureValueType, fileHash: Buffer, text: String) - case secureValueErrorReverseSide(type: Api.SecureValueType, fileHash: Buffer, text: String) - case secureValueErrorSelfie(type: Api.SecureValueType, fileHash: Buffer, text: String) - case secureValueErrorTranslationFile(type: Api.SecureValueType, fileHash: Buffer, text: String) - case secureValueErrorTranslationFiles(type: Api.SecureValueType, fileHash: [Buffer], text: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureValueError(let type, let hash, let text): - if boxed { - buffer.appendInt32(-2036501105) - } - type.serialize(buffer, true) - serializeBytes(hash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorData(let type, let dataHash, let field, let text): - if boxed { - buffer.appendInt32(-391902247) - } - type.serialize(buffer, true) - serializeBytes(dataHash, buffer: buffer, boxed: false) - serializeString(field, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorFile(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(2054162547) - } - type.serialize(buffer, true) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorFiles(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(1717706985) - } - type.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(fileHash.count)) - for item in fileHash { - serializeBytes(item, buffer: buffer, boxed: false) - } - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorFrontSide(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(12467706) - } - type.serialize(buffer, true) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorReverseSide(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(-2037765467) - } - type.serialize(buffer, true) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorSelfie(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(-449327402) - } - type.serialize(buffer, true) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorTranslationFile(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(-1592506512) - } - type.serialize(buffer, true) - serializeBytes(fileHash, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - case .secureValueErrorTranslationFiles(let type, let fileHash, let text): - if boxed { - buffer.appendInt32(878931416) - } - type.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(fileHash.count)) - for item in fileHash { - serializeBytes(item, buffer: buffer, boxed: false) - } - serializeString(text, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureValueError(let type, let hash, let text): - return ("secureValueError", [("type", type as Any), ("hash", hash as Any), ("text", text as Any)]) - case .secureValueErrorData(let type, let dataHash, let field, let text): - return ("secureValueErrorData", [("type", type as Any), ("dataHash", dataHash as Any), ("field", field as Any), ("text", text as Any)]) - case .secureValueErrorFile(let type, let fileHash, let text): - return ("secureValueErrorFile", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorFiles(let type, let fileHash, let text): - return ("secureValueErrorFiles", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorFrontSide(let type, let fileHash, let text): - return ("secureValueErrorFrontSide", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorReverseSide(let type, let fileHash, let text): - return ("secureValueErrorReverseSide", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorSelfie(let type, let fileHash, let text): - return ("secureValueErrorSelfie", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorTranslationFile(let type, let fileHash, let text): - return ("secureValueErrorTranslationFile", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - case .secureValueErrorTranslationFiles(let type, let fileHash, let text): - return ("secureValueErrorTranslationFiles", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) - } - } - - public static func parse_secureValueError(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueError(type: _1!, hash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorData(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.SecureValueError.secureValueErrorData(type: _1!, dataHash: _2!, field: _3!, text: _4!) - } - else { - return nil - } - } - public static func parse_secureValueErrorFile(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFile(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorFiles(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: [Buffer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) - } - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFiles(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorFrontSide(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFrontSide(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorReverseSide(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorReverseSide(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorSelfie(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorSelfie(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorTranslationFile(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorTranslationFile(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - public static func parse_secureValueErrorTranslationFiles(_ reader: BufferReader) -> SecureValueError? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: [Buffer]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) - } - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SecureValueHash: TypeConstructorDescription { - case secureValueHash(type: Api.SecureValueType, hash: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureValueHash(let type, let hash): - if boxed { - buffer.appendInt32(-316748368) - } - type.serialize(buffer, true) - serializeBytes(hash, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureValueHash(let type, let hash): - return ("secureValueHash", [("type", type as Any), ("hash", hash as Any)]) - } - } - - public static func parse_secureValueHash(_ reader: BufferReader) -> SecureValueHash? { - var _1: Api.SecureValueType? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType - } - var _2: Buffer? - _2 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SecureValueType: TypeConstructorDescription { - case secureValueTypeAddress - case secureValueTypeBankStatement - case secureValueTypeDriverLicense - case secureValueTypeEmail - case secureValueTypeIdentityCard - case secureValueTypeInternalPassport - case secureValueTypePassport - case secureValueTypePassportRegistration - case secureValueTypePersonalDetails - case secureValueTypePhone - case secureValueTypeRentalAgreement - case secureValueTypeTemporaryRegistration - case secureValueTypeUtilityBill - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .secureValueTypeAddress: - if boxed { - buffer.appendInt32(-874308058) - } - - break - case .secureValueTypeBankStatement: - if boxed { - buffer.appendInt32(-1995211763) - } - - break - case .secureValueTypeDriverLicense: - if boxed { - buffer.appendInt32(115615172) - } - - break - case .secureValueTypeEmail: - if boxed { - buffer.appendInt32(-1908627474) - } - - break - case .secureValueTypeIdentityCard: - if boxed { - buffer.appendInt32(-1596951477) - } - - break - case .secureValueTypeInternalPassport: - if boxed { - buffer.appendInt32(-1717268701) - } - - break - case .secureValueTypePassport: - if boxed { - buffer.appendInt32(1034709504) - } - - break - case .secureValueTypePassportRegistration: - if boxed { - buffer.appendInt32(-1713143702) - } - - break - case .secureValueTypePersonalDetails: - if boxed { - buffer.appendInt32(-1658158621) - } - - break - case .secureValueTypePhone: - if boxed { - buffer.appendInt32(-1289704741) - } - - break - case .secureValueTypeRentalAgreement: - if boxed { - buffer.appendInt32(-1954007928) - } - - break - case .secureValueTypeTemporaryRegistration: - if boxed { - buffer.appendInt32(-368907213) - } - - break - case .secureValueTypeUtilityBill: - if boxed { - buffer.appendInt32(-63531698) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .secureValueTypeAddress: - return ("secureValueTypeAddress", []) - case .secureValueTypeBankStatement: - return ("secureValueTypeBankStatement", []) - case .secureValueTypeDriverLicense: - return ("secureValueTypeDriverLicense", []) - case .secureValueTypeEmail: - return ("secureValueTypeEmail", []) - case .secureValueTypeIdentityCard: - return ("secureValueTypeIdentityCard", []) - case .secureValueTypeInternalPassport: - return ("secureValueTypeInternalPassport", []) - case .secureValueTypePassport: - return ("secureValueTypePassport", []) - case .secureValueTypePassportRegistration: - return ("secureValueTypePassportRegistration", []) - case .secureValueTypePersonalDetails: - return ("secureValueTypePersonalDetails", []) - case .secureValueTypePhone: - return ("secureValueTypePhone", []) - case .secureValueTypeRentalAgreement: - return ("secureValueTypeRentalAgreement", []) - case .secureValueTypeTemporaryRegistration: - return ("secureValueTypeTemporaryRegistration", []) - case .secureValueTypeUtilityBill: - return ("secureValueTypeUtilityBill", []) - } - } - - public static func parse_secureValueTypeAddress(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeAddress - } - public static func parse_secureValueTypeBankStatement(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeBankStatement - } - public static func parse_secureValueTypeDriverLicense(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeDriverLicense - } - public static func parse_secureValueTypeEmail(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeEmail - } - public static func parse_secureValueTypeIdentityCard(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeIdentityCard - } - public static func parse_secureValueTypeInternalPassport(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeInternalPassport - } - public static func parse_secureValueTypePassport(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypePassport - } - public static func parse_secureValueTypePassportRegistration(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypePassportRegistration - } - public static func parse_secureValueTypePersonalDetails(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypePersonalDetails - } - public static func parse_secureValueTypePhone(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypePhone - } - public static func parse_secureValueTypeRentalAgreement(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeRentalAgreement - } - public static func parse_secureValueTypeTemporaryRegistration(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeTemporaryRegistration - } - public static func parse_secureValueTypeUtilityBill(_ reader: BufferReader) -> SecureValueType? { - return Api.SecureValueType.secureValueTypeUtilityBill - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index cab7fede4f..b3e42f92db 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,3 +1,785 @@ +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) + case secureRequiredTypeOneOf(types: [Api.SecureRequiredType]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureRequiredType(let flags, let type): + if boxed { + buffer.appendInt32(-2103600678) + } + serializeInt32(flags, buffer: buffer, boxed: false) + type.serialize(buffer, true) + break + case .secureRequiredTypeOneOf(let types): + if boxed { + buffer.appendInt32(41187252) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(types.count)) + for item in types { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureRequiredType(let flags, let type): + return ("secureRequiredType", [("flags", flags as Any), ("type", type as Any)]) + case .secureRequiredTypeOneOf(let types): + return ("secureRequiredTypeOneOf", [("types", types as Any)]) + } + } + + public static func parse_secureRequiredType(_ reader: BufferReader) -> SecureRequiredType? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.SecureValueType? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.SecureRequiredType.secureRequiredType(flags: _1!, type: _2!) + } + else { + return nil + } + } + public static func parse_secureRequiredTypeOneOf(_ reader: BufferReader) -> SecureRequiredType? { + var _1: [Api.SecureRequiredType]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.SecureRequiredType.secureRequiredTypeOneOf(types: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureSecretSettings: TypeConstructorDescription { + case secureSecretSettings(secureAlgo: Api.SecurePasswordKdfAlgo, secureSecret: Buffer, secureSecretId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureSecretSettings(let secureAlgo, let secureSecret, let secureSecretId): + if boxed { + buffer.appendInt32(354925740) + } + secureAlgo.serialize(buffer, true) + serializeBytes(secureSecret, buffer: buffer, boxed: false) + serializeInt64(secureSecretId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureSecretSettings(let secureAlgo, let secureSecret, let secureSecretId): + return ("secureSecretSettings", [("secureAlgo", secureAlgo as Any), ("secureSecret", secureSecret as Any), ("secureSecretId", secureSecretId as Any)]) + } + } + + public static func parse_secureSecretSettings(_ reader: BufferReader) -> SecureSecretSettings? { + var _1: Api.SecurePasswordKdfAlgo? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecurePasswordKdfAlgo + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureSecretSettings.secureSecretSettings(secureAlgo: _1!, secureSecret: _2!, secureSecretId: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureValue: TypeConstructorDescription { + case secureValue(flags: Int32, type: Api.SecureValueType, data: Api.SecureData?, frontSide: Api.SecureFile?, reverseSide: Api.SecureFile?, selfie: Api.SecureFile?, translation: [Api.SecureFile]?, files: [Api.SecureFile]?, plainData: Api.SecurePlainData?, hash: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData, let hash): + if boxed { + buffer.appendInt32(411017418) + } + serializeInt32(flags, buffer: buffer, boxed: false) + type.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {data!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {frontSide!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {reverseSide!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {selfie!.serialize(buffer, true)} + if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(translation!.count)) + for item in translation! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(files!.count)) + for item in files! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 5) != 0 {plainData!.serialize(buffer, true)} + serializeBytes(hash, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData, let hash): + return ("secureValue", [("flags", flags as Any), ("type", type as Any), ("data", data as Any), ("frontSide", frontSide as Any), ("reverseSide", reverseSide as Any), ("selfie", selfie as Any), ("translation", translation as Any), ("files", files as Any), ("plainData", plainData as Any), ("hash", hash as Any)]) + } + } + + public static func parse_secureValue(_ reader: BufferReader) -> SecureValue? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.SecureValueType? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _3: Api.SecureData? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.SecureData + } } + var _4: Api.SecureFile? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.SecureFile + } } + var _5: Api.SecureFile? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.SecureFile + } } + var _6: Api.SecureFile? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.SecureFile + } } + var _7: [Api.SecureFile]? + if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureFile.self) + } } + var _8: [Api.SecureFile]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureFile.self) + } } + var _9: Api.SecurePlainData? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.SecurePlainData + } } + var _10: Buffer? + _10 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil + let _c10 = _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.SecureValue.secureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9, hash: _10!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureValueError: TypeConstructorDescription { + case secureValueError(type: Api.SecureValueType, hash: Buffer, text: String) + case secureValueErrorData(type: Api.SecureValueType, dataHash: Buffer, field: String, text: String) + case secureValueErrorFile(type: Api.SecureValueType, fileHash: Buffer, text: String) + case secureValueErrorFiles(type: Api.SecureValueType, fileHash: [Buffer], text: String) + case secureValueErrorFrontSide(type: Api.SecureValueType, fileHash: Buffer, text: String) + case secureValueErrorReverseSide(type: Api.SecureValueType, fileHash: Buffer, text: String) + case secureValueErrorSelfie(type: Api.SecureValueType, fileHash: Buffer, text: String) + case secureValueErrorTranslationFile(type: Api.SecureValueType, fileHash: Buffer, text: String) + case secureValueErrorTranslationFiles(type: Api.SecureValueType, fileHash: [Buffer], text: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureValueError(let type, let hash, let text): + if boxed { + buffer.appendInt32(-2036501105) + } + type.serialize(buffer, true) + serializeBytes(hash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorData(let type, let dataHash, let field, let text): + if boxed { + buffer.appendInt32(-391902247) + } + type.serialize(buffer, true) + serializeBytes(dataHash, buffer: buffer, boxed: false) + serializeString(field, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorFile(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(2054162547) + } + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorFiles(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(1717706985) + } + type.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(fileHash.count)) + for item in fileHash { + serializeBytes(item, buffer: buffer, boxed: false) + } + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorFrontSide(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(12467706) + } + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorReverseSide(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(-2037765467) + } + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorSelfie(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(-449327402) + } + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorTranslationFile(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(-1592506512) + } + type.serialize(buffer, true) + serializeBytes(fileHash, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + case .secureValueErrorTranslationFiles(let type, let fileHash, let text): + if boxed { + buffer.appendInt32(878931416) + } + type.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(fileHash.count)) + for item in fileHash { + serializeBytes(item, buffer: buffer, boxed: false) + } + serializeString(text, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureValueError(let type, let hash, let text): + return ("secureValueError", [("type", type as Any), ("hash", hash as Any), ("text", text as Any)]) + case .secureValueErrorData(let type, let dataHash, let field, let text): + return ("secureValueErrorData", [("type", type as Any), ("dataHash", dataHash as Any), ("field", field as Any), ("text", text as Any)]) + case .secureValueErrorFile(let type, let fileHash, let text): + return ("secureValueErrorFile", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorFiles(let type, let fileHash, let text): + return ("secureValueErrorFiles", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorFrontSide(let type, let fileHash, let text): + return ("secureValueErrorFrontSide", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorReverseSide(let type, let fileHash, let text): + return ("secureValueErrorReverseSide", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorSelfie(let type, let fileHash, let text): + return ("secureValueErrorSelfie", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorTranslationFile(let type, let fileHash, let text): + return ("secureValueErrorTranslationFile", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + case .secureValueErrorTranslationFiles(let type, let fileHash, let text): + return ("secureValueErrorTranslationFiles", [("type", type as Any), ("fileHash", fileHash as Any), ("text", text as Any)]) + } + } + + public static func parse_secureValueError(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueError(type: _1!, hash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorData(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.SecureValueError.secureValueErrorData(type: _1!, dataHash: _2!, field: _3!, text: _4!) + } + else { + return nil + } + } + public static func parse_secureValueErrorFile(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorFile(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorFiles(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: [Buffer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorFiles(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorFrontSide(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorFrontSide(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorReverseSide(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorReverseSide(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorSelfie(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorSelfie(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorTranslationFile(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorTranslationFile(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + public static func parse_secureValueErrorTranslationFiles(_ reader: BufferReader) -> SecureValueError? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: [Buffer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureValueHash: TypeConstructorDescription { + case secureValueHash(type: Api.SecureValueType, hash: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureValueHash(let type, let hash): + if boxed { + buffer.appendInt32(-316748368) + } + type.serialize(buffer, true) + serializeBytes(hash, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureValueHash(let type, let hash): + return ("secureValueHash", [("type", type as Any), ("hash", hash as Any)]) + } + } + + public static func parse_secureValueHash(_ reader: BufferReader) -> SecureValueHash? { + var _1: Api.SecureValueType? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.SecureValueType + } + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SecureValueType: TypeConstructorDescription { + case secureValueTypeAddress + case secureValueTypeBankStatement + case secureValueTypeDriverLicense + case secureValueTypeEmail + case secureValueTypeIdentityCard + case secureValueTypeInternalPassport + case secureValueTypePassport + case secureValueTypePassportRegistration + case secureValueTypePersonalDetails + case secureValueTypePhone + case secureValueTypeRentalAgreement + case secureValueTypeTemporaryRegistration + case secureValueTypeUtilityBill + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .secureValueTypeAddress: + if boxed { + buffer.appendInt32(-874308058) + } + + break + case .secureValueTypeBankStatement: + if boxed { + buffer.appendInt32(-1995211763) + } + + break + case .secureValueTypeDriverLicense: + if boxed { + buffer.appendInt32(115615172) + } + + break + case .secureValueTypeEmail: + if boxed { + buffer.appendInt32(-1908627474) + } + + break + case .secureValueTypeIdentityCard: + if boxed { + buffer.appendInt32(-1596951477) + } + + break + case .secureValueTypeInternalPassport: + if boxed { + buffer.appendInt32(-1717268701) + } + + break + case .secureValueTypePassport: + if boxed { + buffer.appendInt32(1034709504) + } + + break + case .secureValueTypePassportRegistration: + if boxed { + buffer.appendInt32(-1713143702) + } + + break + case .secureValueTypePersonalDetails: + if boxed { + buffer.appendInt32(-1658158621) + } + + break + case .secureValueTypePhone: + if boxed { + buffer.appendInt32(-1289704741) + } + + break + case .secureValueTypeRentalAgreement: + if boxed { + buffer.appendInt32(-1954007928) + } + + break + case .secureValueTypeTemporaryRegistration: + if boxed { + buffer.appendInt32(-368907213) + } + + break + case .secureValueTypeUtilityBill: + if boxed { + buffer.appendInt32(-63531698) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .secureValueTypeAddress: + return ("secureValueTypeAddress", []) + case .secureValueTypeBankStatement: + return ("secureValueTypeBankStatement", []) + case .secureValueTypeDriverLicense: + return ("secureValueTypeDriverLicense", []) + case .secureValueTypeEmail: + return ("secureValueTypeEmail", []) + case .secureValueTypeIdentityCard: + return ("secureValueTypeIdentityCard", []) + case .secureValueTypeInternalPassport: + return ("secureValueTypeInternalPassport", []) + case .secureValueTypePassport: + return ("secureValueTypePassport", []) + case .secureValueTypePassportRegistration: + return ("secureValueTypePassportRegistration", []) + case .secureValueTypePersonalDetails: + return ("secureValueTypePersonalDetails", []) + case .secureValueTypePhone: + return ("secureValueTypePhone", []) + case .secureValueTypeRentalAgreement: + return ("secureValueTypeRentalAgreement", []) + case .secureValueTypeTemporaryRegistration: + return ("secureValueTypeTemporaryRegistration", []) + case .secureValueTypeUtilityBill: + return ("secureValueTypeUtilityBill", []) + } + } + + public static func parse_secureValueTypeAddress(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeAddress + } + public static func parse_secureValueTypeBankStatement(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeBankStatement + } + public static func parse_secureValueTypeDriverLicense(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeDriverLicense + } + public static func parse_secureValueTypeEmail(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeEmail + } + public static func parse_secureValueTypeIdentityCard(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeIdentityCard + } + public static func parse_secureValueTypeInternalPassport(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeInternalPassport + } + public static func parse_secureValueTypePassport(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypePassport + } + public static func parse_secureValueTypePassportRegistration(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypePassportRegistration + } + public static func parse_secureValueTypePersonalDetails(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypePersonalDetails + } + public static func parse_secureValueTypePhone(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypePhone + } + public static func parse_secureValueTypeRentalAgreement(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeRentalAgreement + } + public static func parse_secureValueTypeTemporaryRegistration(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeTemporaryRegistration + } + public static func parse_secureValueTypeUtilityBill(_ reader: BufferReader) -> SecureValueType? { + return Api.SecureValueType.secureValueTypeUtilityBill + } + + } +} public extension Api { enum SendAsPeer: TypeConstructorDescription { case sendAsPeer(flags: Int32, peer: Api.Peer) @@ -346,759 +1128,3 @@ public extension Api { } } -public extension Api { - enum ShippingOption: TypeConstructorDescription { - case shippingOption(id: String, title: String, prices: [Api.LabeledPrice]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .shippingOption(let id, let title, let prices): - if boxed { - buffer.appendInt32(-1239335713) - } - serializeString(id, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(prices.count)) - for item in prices { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .shippingOption(let id, let title, let prices): - return ("shippingOption", [("id", id as Any), ("title", title as Any), ("prices", prices as Any)]) - } - } - - public static func parse_shippingOption(_ reader: BufferReader) -> ShippingOption? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - var _3: [Api.LabeledPrice]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LabeledPrice.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ShippingOption.shippingOption(id: _1!, title: _2!, prices: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SmsJob: TypeConstructorDescription { - case smsJob(jobId: String, phoneNumber: String, text: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .smsJob(let jobId, let phoneNumber, let text): - if boxed { - buffer.appendInt32(-425595208) - } - serializeString(jobId, buffer: buffer, boxed: false) - serializeString(phoneNumber, buffer: buffer, boxed: false) - serializeString(text, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .smsJob(let jobId, let phoneNumber, let text): - return ("smsJob", [("jobId", jobId as Any), ("phoneNumber", phoneNumber as Any), ("text", text as Any)]) - } - } - - public static func parse_smsJob(_ reader: BufferReader) -> SmsJob? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SmsJob.smsJob(jobId: _1!, phoneNumber: _2!, text: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - indirect enum SponsoredMessage: TypeConstructorDescription { - case sponsoredMessage(flags: Int32, randomId: Buffer, url: String, title: String, message: String, entities: [Api.MessageEntity]?, photo: Api.Photo?, media: Api.MessageMedia?, color: Api.PeerColor?, buttonText: String, sponsorInfo: String?, additionalInfo: String?, minDisplayDuration: Int32?, maxDisplayDuration: Int32?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo, let minDisplayDuration, let maxDisplayDuration): - if boxed { - buffer.appendInt32(2109703795) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeBytes(randomId, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 6) != 0 {photo!.serialize(buffer, true)} - if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 13) != 0 {color!.serialize(buffer, true)} - serializeString(buttonText, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 15) != 0 {serializeInt32(minDisplayDuration!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 15) != 0 {serializeInt32(maxDisplayDuration!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo, let minDisplayDuration, let maxDisplayDuration): - return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("url", url as Any), ("title", title as Any), ("message", message as Any), ("entities", entities as Any), ("photo", photo as Any), ("media", media as Any), ("color", color as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any), ("minDisplayDuration", minDisplayDuration as Any), ("maxDisplayDuration", maxDisplayDuration as Any)]) - } - } - - public static func parse_sponsoredMessage(_ reader: BufferReader) -> SponsoredMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } - var _7: Api.Photo? - if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.Photo - } } - var _8: Api.MessageMedia? - if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.MessageMedia - } } - var _9: Api.PeerColor? - if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PeerColor - } } - var _10: String? - _10 = parseString(reader) - var _11: String? - if Int(_1!) & Int(1 << 7) != 0 {_11 = parseString(reader) } - var _12: String? - if Int(_1!) & Int(1 << 8) != 0 {_12 = parseString(reader) } - var _13: Int32? - if Int(_1!) & Int(1 << 15) != 0 {_13 = reader.readInt32() } - var _14: Int32? - if Int(_1!) & Int(1 << 15) != 0 {_14 = reader.readInt32() } - 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 << 1) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 14) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 13) == 0) || _9 != nil - let _c10 = _10 != nil - let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, media: _8, color: _9, buttonText: _10!, sponsorInfo: _11, additionalInfo: _12, minDisplayDuration: _13, maxDisplayDuration: _14) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SponsoredMessageReportOption: TypeConstructorDescription { - case sponsoredMessageReportOption(text: String, option: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sponsoredMessageReportOption(let text, let option): - if boxed { - buffer.appendInt32(1124938064) - } - serializeString(text, buffer: buffer, boxed: false) - serializeBytes(option, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sponsoredMessageReportOption(let text, let option): - return ("sponsoredMessageReportOption", [("text", text as Any), ("option", option as Any)]) - } - } - - public static func parse_sponsoredMessageReportOption(_ reader: BufferReader) -> SponsoredMessageReportOption? { - var _1: String? - _1 = parseString(reader) - var _2: Buffer? - _2 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SponsoredMessageReportOption.sponsoredMessageReportOption(text: _1!, option: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum SponsoredPeer: TypeConstructorDescription { - case sponsoredPeer(flags: Int32, randomId: Buffer, peer: Api.Peer, sponsorInfo: String?, additionalInfo: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sponsoredPeer(let flags, let randomId, let peer, let sponsorInfo, let additionalInfo): - if boxed { - buffer.appendInt32(-963180333) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeBytes(randomId, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sponsoredPeer(let flags, let randomId, let peer, let sponsorInfo, let additionalInfo): - return ("sponsoredPeer", [("flags", flags as Any), ("randomId", randomId as Any), ("peer", peer as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)]) - } - } - - public static func parse_sponsoredPeer(_ reader: BufferReader) -> SponsoredPeer? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Buffer? - _2 = parseBytes(reader) - var _3: Api.Peer? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _4: String? - if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } - var _5: String? - if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.SponsoredPeer.sponsoredPeer(flags: _1!, randomId: _2!, peer: _3!, sponsorInfo: _4, additionalInfo: _5) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StarGift: TypeConstructorDescription { - case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?) - case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains): - if boxed { - buffer.appendInt32(12386139) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - sticker.serialize(buffer, true) - serializeInt64(stars, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(availabilityRemains!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(availabilityTotal!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt64(availabilityResale!, buffer: buffer, boxed: false)} - serializeInt64(convertStars, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(firstSaleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(lastSaleDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(upgradeStars!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt64(resellMinStars!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 5) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 6) != 0 {releasedBy!.serialize(buffer, true)} - if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserTotal!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)} - break - case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy): - if boxed { - buffer.appendInt32(975654224) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(slug, buffer: buffer, boxed: false) - serializeInt32(num, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {ownerId!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(ownerName!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(ownerAddress!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes.count)) - for item in attributes { - item.serialize(buffer, true) - } - serializeInt32(availabilityIssued, buffer: buffer, boxed: false) - serializeInt32(availabilityTotal, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeString(giftAddress!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(resellAmount!.count)) - for item in resellAmount! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains): - return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any)]) - case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy): - return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any)]) - } - } - - public static func parse_starGift(_ reader: BufferReader) -> StarGift? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Api.Document? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Document - } - var _4: Int64? - _4 = reader.readInt64() - var _5: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } - var _6: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } - var _7: Int64? - if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt64() } - var _8: Int64? - _8 = reader.readInt64() - var _9: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_9 = reader.readInt32() } - var _10: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_10 = reader.readInt32() } - var _11: Int64? - if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } - var _12: Int64? - if Int(_1!) & Int(1 << 4) != 0 {_12 = reader.readInt64() } - var _13: String? - if Int(_1!) & Int(1 << 5) != 0 {_13 = parseString(reader) } - var _14: Api.Peer? - if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.Peer - } } - var _15: Int32? - if Int(_1!) & Int(1 << 8) != 0 {_15 = reader.readInt32() } - var _16: Int32? - if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 1) == 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 = (Int(_1!) & Int(1 << 4) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 5) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 8) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { - return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16) - } - else { - return nil - } - } - public static func parse_starGiftUnique(_ reader: BufferReader) -> StarGift? { - 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: Api.Peer? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.Peer - } } - 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: [Api.StarGiftAttribute]? - if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self) - } - var _10: Int32? - _10 = reader.readInt32() - var _11: Int32? - _11 = reader.readInt32() - var _12: String? - if Int(_1!) & Int(1 << 3) != 0 {_12 = parseString(reader) } - var _13: [Api.StarsAmount]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsAmount.self) - } } - var _14: Api.Peer? - if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.Peer - } } - 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 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 4) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 5) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!, giftAddress: _12, resellAmount: _13, releasedBy: _14) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StarGiftAttribute: TypeConstructorDescription { - case starGiftAttributeBackdrop(name: String, backdropId: Int32, centerColor: Int32, edgeColor: Int32, patternColor: Int32, textColor: Int32, rarityPermille: Int32) - case starGiftAttributeModel(name: String, document: Api.Document, rarityPermille: Int32) - case starGiftAttributeOriginalDetails(flags: Int32, senderId: Api.Peer?, recipientId: Api.Peer, date: Int32, message: Api.TextWithEntities?) - case starGiftAttributePattern(name: String, document: Api.Document, rarityPermille: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starGiftAttributeBackdrop(let name, let backdropId, let centerColor, let edgeColor, let patternColor, let textColor, let rarityPermille): - if boxed { - buffer.appendInt32(-650279524) - } - serializeString(name, buffer: buffer, boxed: false) - serializeInt32(backdropId, buffer: buffer, boxed: false) - serializeInt32(centerColor, buffer: buffer, boxed: false) - serializeInt32(edgeColor, buffer: buffer, boxed: false) - serializeInt32(patternColor, buffer: buffer, boxed: false) - serializeInt32(textColor, buffer: buffer, boxed: false) - serializeInt32(rarityPermille, buffer: buffer, boxed: false) - break - case .starGiftAttributeModel(let name, let document, let rarityPermille): - if boxed { - buffer.appendInt32(970559507) - } - serializeString(name, buffer: buffer, boxed: false) - document.serialize(buffer, true) - serializeInt32(rarityPermille, buffer: buffer, boxed: false) - break - case .starGiftAttributeOriginalDetails(let flags, let senderId, let recipientId, let date, let message): - if boxed { - buffer.appendInt32(-524291476) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {senderId!.serialize(buffer, true)} - recipientId.serialize(buffer, true) - serializeInt32(date, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)} - break - case .starGiftAttributePattern(let name, let document, let rarityPermille): - if boxed { - buffer.appendInt32(330104601) - } - serializeString(name, buffer: buffer, boxed: false) - document.serialize(buffer, true) - serializeInt32(rarityPermille, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starGiftAttributeBackdrop(let name, let backdropId, let centerColor, let edgeColor, let patternColor, let textColor, let rarityPermille): - return ("starGiftAttributeBackdrop", [("name", name as Any), ("backdropId", backdropId as Any), ("centerColor", centerColor as Any), ("edgeColor", edgeColor as Any), ("patternColor", patternColor as Any), ("textColor", textColor as Any), ("rarityPermille", rarityPermille as Any)]) - case .starGiftAttributeModel(let name, let document, let rarityPermille): - return ("starGiftAttributeModel", [("name", name as Any), ("document", document as Any), ("rarityPermille", rarityPermille as Any)]) - case .starGiftAttributeOriginalDetails(let flags, let senderId, let recipientId, let date, let message): - return ("starGiftAttributeOriginalDetails", [("flags", flags as Any), ("senderId", senderId as Any), ("recipientId", recipientId as Any), ("date", date as Any), ("message", message as Any)]) - case .starGiftAttributePattern(let name, let document, let rarityPermille): - return ("starGiftAttributePattern", [("name", name as Any), ("document", document as Any), ("rarityPermille", rarityPermille as Any)]) - } - } - - public static func parse_starGiftAttributeBackdrop(_ reader: BufferReader) -> StarGiftAttribute? { - var _1: String? - _1 = parseString(reader) - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() - 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.StarGiftAttribute.starGiftAttributeBackdrop(name: _1!, backdropId: _2!, centerColor: _3!, edgeColor: _4!, patternColor: _5!, textColor: _6!, rarityPermille: _7!) - } - else { - return nil - } - } - public static func parse_starGiftAttributeModel(_ reader: BufferReader) -> StarGiftAttribute? { - var _1: String? - _1 = parseString(reader) - var _2: Api.Document? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Document - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftAttribute.starGiftAttributeModel(name: _1!, document: _2!, rarityPermille: _3!) - } - else { - return nil - } - } - public static func parse_starGiftAttributeOriginalDetails(_ reader: BufferReader) -> StarGiftAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.Peer? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Peer - } } - var _3: Api.Peer? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _4: Int32? - _4 = reader.readInt32() - var _5: Api.TextWithEntities? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.TextWithEntities - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarGiftAttribute.starGiftAttributeOriginalDetails(flags: _1!, senderId: _2, recipientId: _3!, date: _4!, message: _5) - } - else { - return nil - } - } - public static func parse_starGiftAttributePattern(_ reader: BufferReader) -> StarGiftAttribute? { - var _1: String? - _1 = parseString(reader) - var _2: Api.Document? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.Document - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftAttribute.starGiftAttributePattern(name: _1!, document: _2!, rarityPermille: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StarGiftAttributeCounter: TypeConstructorDescription { - case starGiftAttributeCounter(attribute: Api.StarGiftAttributeId, count: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starGiftAttributeCounter(let attribute, let count): - if boxed { - buffer.appendInt32(783398488) - } - attribute.serialize(buffer, true) - serializeInt32(count, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starGiftAttributeCounter(let attribute, let count): - return ("starGiftAttributeCounter", [("attribute", attribute as Any), ("count", count as Any)]) - } - } - - public static func parse_starGiftAttributeCounter(_ reader: BufferReader) -> StarGiftAttributeCounter? { - var _1: Api.StarGiftAttributeId? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StarGiftAttributeId - } - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarGiftAttributeCounter.starGiftAttributeCounter(attribute: _1!, count: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StarGiftAttributeId: TypeConstructorDescription { - case starGiftAttributeIdBackdrop(backdropId: Int32) - case starGiftAttributeIdModel(documentId: Int64) - case starGiftAttributeIdPattern(documentId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starGiftAttributeIdBackdrop(let backdropId): - if boxed { - buffer.appendInt32(520210263) - } - serializeInt32(backdropId, buffer: buffer, boxed: false) - break - case .starGiftAttributeIdModel(let documentId): - if boxed { - buffer.appendInt32(1219145276) - } - serializeInt64(documentId, buffer: buffer, boxed: false) - break - case .starGiftAttributeIdPattern(let documentId): - if boxed { - buffer.appendInt32(1242965043) - } - serializeInt64(documentId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starGiftAttributeIdBackdrop(let backdropId): - return ("starGiftAttributeIdBackdrop", [("backdropId", backdropId as Any)]) - case .starGiftAttributeIdModel(let documentId): - return ("starGiftAttributeIdModel", [("documentId", documentId as Any)]) - case .starGiftAttributeIdPattern(let documentId): - return ("starGiftAttributeIdPattern", [("documentId", documentId as Any)]) - } - } - - public static func parse_starGiftAttributeIdBackdrop(_ reader: BufferReader) -> StarGiftAttributeId? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdBackdrop(backdropId: _1!) - } - else { - return nil - } - } - public static func parse_starGiftAttributeIdModel(_ reader: BufferReader) -> StarGiftAttributeId? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdModel(documentId: _1!) - } - else { - return nil - } - } - public static func parse_starGiftAttributeIdPattern(_ reader: BufferReader) -> StarGiftAttributeId? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdPattern(documentId: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 342ffa7fa5..25f6c4b10d 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,781 @@ +public extension Api { + enum ShippingOption: TypeConstructorDescription { + case shippingOption(id: String, title: String, prices: [Api.LabeledPrice]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .shippingOption(let id, let title, let prices): + if boxed { + buffer.appendInt32(-1239335713) + } + serializeString(id, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(prices.count)) + for item in prices { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .shippingOption(let id, let title, let prices): + return ("shippingOption", [("id", id as Any), ("title", title as Any), ("prices", prices as Any)]) + } + } + + public static func parse_shippingOption(_ reader: BufferReader) -> ShippingOption? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: [Api.LabeledPrice]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LabeledPrice.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.ShippingOption.shippingOption(id: _1!, title: _2!, prices: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SmsJob: TypeConstructorDescription { + case smsJob(jobId: String, phoneNumber: String, text: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .smsJob(let jobId, let phoneNumber, let text): + if boxed { + buffer.appendInt32(-425595208) + } + serializeString(jobId, buffer: buffer, boxed: false) + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .smsJob(let jobId, let phoneNumber, let text): + return ("smsJob", [("jobId", jobId as Any), ("phoneNumber", phoneNumber as Any), ("text", text as Any)]) + } + } + + public static func parse_smsJob(_ reader: BufferReader) -> SmsJob? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SmsJob.smsJob(jobId: _1!, phoneNumber: _2!, text: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum SponsoredMessage: TypeConstructorDescription { + case sponsoredMessage(flags: Int32, randomId: Buffer, url: String, title: String, message: String, entities: [Api.MessageEntity]?, photo: Api.Photo?, media: Api.MessageMedia?, color: Api.PeerColor?, buttonText: String, sponsorInfo: String?, additionalInfo: String?, minDisplayDuration: Int32?, maxDisplayDuration: Int32?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo, let minDisplayDuration, let maxDisplayDuration): + if boxed { + buffer.appendInt32(2109703795) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeBytes(randomId, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 6) != 0 {photo!.serialize(buffer, true)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 13) != 0 {color!.serialize(buffer, true)} + serializeString(buttonText, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(minDisplayDuration!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(maxDisplayDuration!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sponsoredMessage(let flags, let randomId, let url, let title, let message, let entities, let photo, let media, let color, let buttonText, let sponsorInfo, let additionalInfo, let minDisplayDuration, let maxDisplayDuration): + return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("url", url as Any), ("title", title as Any), ("message", message as Any), ("entities", entities as Any), ("photo", photo as Any), ("media", media as Any), ("color", color as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any), ("minDisplayDuration", minDisplayDuration as Any), ("maxDisplayDuration", maxDisplayDuration as Any)]) + } + } + + public static func parse_sponsoredMessage(_ reader: BufferReader) -> SponsoredMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _7: Api.Photo? + if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Photo + } } + var _8: Api.MessageMedia? + if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.MessageMedia + } } + var _9: Api.PeerColor? + if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PeerColor + } } + var _10: String? + _10 = parseString(reader) + var _11: String? + if Int(_1!) & Int(1 << 7) != 0 {_11 = parseString(reader) } + var _12: String? + if Int(_1!) & Int(1 << 8) != 0 {_12 = parseString(reader) } + var _13: Int32? + if Int(_1!) & Int(1 << 15) != 0 {_13 = reader.readInt32() } + var _14: Int32? + if Int(_1!) & Int(1 << 15) != 0 {_14 = reader.readInt32() } + 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 << 1) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 14) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 13) == 0) || _9 != nil + let _c10 = _10 != nil + let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { + return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, media: _8, color: _9, buttonText: _10!, sponsorInfo: _11, additionalInfo: _12, minDisplayDuration: _13, maxDisplayDuration: _14) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SponsoredMessageReportOption: TypeConstructorDescription { + case sponsoredMessageReportOption(text: String, option: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sponsoredMessageReportOption(let text, let option): + if boxed { + buffer.appendInt32(1124938064) + } + serializeString(text, buffer: buffer, boxed: false) + serializeBytes(option, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sponsoredMessageReportOption(let text, let option): + return ("sponsoredMessageReportOption", [("text", text as Any), ("option", option as Any)]) + } + } + + public static func parse_sponsoredMessageReportOption(_ reader: BufferReader) -> SponsoredMessageReportOption? { + var _1: String? + _1 = parseString(reader) + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.SponsoredMessageReportOption.sponsoredMessageReportOption(text: _1!, option: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum SponsoredPeer: TypeConstructorDescription { + case sponsoredPeer(flags: Int32, randomId: Buffer, peer: Api.Peer, sponsorInfo: String?, additionalInfo: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sponsoredPeer(let flags, let randomId, let peer, let sponsorInfo, let additionalInfo): + if boxed { + buffer.appendInt32(-963180333) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeBytes(randomId, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sponsoredPeer(let flags, let randomId, let peer, let sponsorInfo, let additionalInfo): + return ("sponsoredPeer", [("flags", flags as Any), ("randomId", randomId as Any), ("peer", peer as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)]) + } + } + + public static func parse_sponsoredPeer(_ reader: BufferReader) -> SponsoredPeer? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Api.Peer? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: String? + if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.SponsoredPeer.sponsoredPeer(flags: _1!, randomId: _2!, peer: _3!, sponsorInfo: _4, additionalInfo: _5) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StarGift: TypeConstructorDescription { + case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?) + case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, themePeer: Api.Peer?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate): + if boxed { + buffer.appendInt32(-2136190013) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + sticker.serialize(buffer, true) + serializeInt64(stars, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(availabilityRemains!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(availabilityTotal!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt64(availabilityResale!, buffer: buffer, boxed: false)} + serializeInt64(convertStars, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(firstSaleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(lastSaleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(upgradeStars!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt64(resellMinStars!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {releasedBy!.serialize(buffer, true)} + if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserTotal!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {serializeInt32(lockedUntilDate!, buffer: buffer, boxed: false)} + break + case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer): + if boxed { + buffer.appendInt32(468707429) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(giftId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(slug, buffer: buffer, boxed: false) + serializeInt32(num, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {ownerId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(ownerName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(ownerAddress!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) + } + serializeInt32(availabilityIssued, buffer: buffer, boxed: false) + serializeInt32(availabilityTotal, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeString(giftAddress!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(resellAmount!.count)) + for item in resellAmount! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)} + if Int(flags) & Int(1 << 8) != 0 {serializeInt64(valueAmount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {serializeString(valueCurrency!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 10) != 0 {themePeer!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate): + return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any)]) + case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer): + return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("themePeer", themePeer as Any)]) + } + } + + public static func parse_starGift(_ reader: BufferReader) -> StarGift? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Api.Document? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Document + } + var _4: Int64? + _4 = reader.readInt64() + var _5: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } + var _7: Int64? + if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt64() } + var _8: Int64? + _8 = reader.readInt64() + var _9: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_9 = reader.readInt32() } + var _10: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_10 = reader.readInt32() } + var _11: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } + var _12: Int64? + if Int(_1!) & Int(1 << 4) != 0 {_12 = reader.readInt64() } + var _13: String? + if Int(_1!) & Int(1 << 5) != 0 {_13 = parseString(reader) } + var _14: Api.Peer? + if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { + _14 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _15: Int32? + if Int(_1!) & Int(1 << 8) != 0 {_15 = reader.readInt32() } + var _16: Int32? + if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt32() } + var _17: Int32? + if Int(_1!) & Int(1 << 9) != 0 {_17 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 1) == 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 = (Int(_1!) & Int(1 << 4) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 5) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 8) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16, lockedUntilDate: _17) + } + else { + return nil + } + } + public static func parse_starGiftUnique(_ reader: BufferReader) -> StarGift? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Int32? + _6 = reader.readInt32() + var _7: Api.Peer? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _8: String? + if Int(_1!) & Int(1 << 1) != 0 {_8 = parseString(reader) } + var _9: String? + if Int(_1!) & Int(1 << 2) != 0 {_9 = parseString(reader) } + var _10: [Api.StarGiftAttribute]? + if let _ = reader.readInt32() { + _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self) + } + var _11: Int32? + _11 = reader.readInt32() + var _12: Int32? + _12 = reader.readInt32() + var _13: String? + if Int(_1!) & Int(1 << 3) != 0 {_13 = parseString(reader) } + var _14: [Api.StarsAmount]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsAmount.self) + } } + var _15: Api.Peer? + if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { + _15 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _16: Int64? + if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt64() } + var _17: String? + if Int(_1!) & Int(1 << 8) != 0 {_17 = parseString(reader) } + var _18: Api.Peer? + if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.Peer + } } + 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 << 0) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + let _c13 = (Int(_1!) & Int(1 << 3) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 4) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 5) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 8) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { + return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, themePeer: _18) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StarGiftAttribute: TypeConstructorDescription { + case starGiftAttributeBackdrop(name: String, backdropId: Int32, centerColor: Int32, edgeColor: Int32, patternColor: Int32, textColor: Int32, rarityPermille: Int32) + case starGiftAttributeModel(name: String, document: Api.Document, rarityPermille: Int32) + case starGiftAttributeOriginalDetails(flags: Int32, senderId: Api.Peer?, recipientId: Api.Peer, date: Int32, message: Api.TextWithEntities?) + case starGiftAttributePattern(name: String, document: Api.Document, rarityPermille: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGiftAttributeBackdrop(let name, let backdropId, let centerColor, let edgeColor, let patternColor, let textColor, let rarityPermille): + if boxed { + buffer.appendInt32(-650279524) + } + serializeString(name, buffer: buffer, boxed: false) + serializeInt32(backdropId, buffer: buffer, boxed: false) + serializeInt32(centerColor, buffer: buffer, boxed: false) + serializeInt32(edgeColor, buffer: buffer, boxed: false) + serializeInt32(patternColor, buffer: buffer, boxed: false) + serializeInt32(textColor, buffer: buffer, boxed: false) + serializeInt32(rarityPermille, buffer: buffer, boxed: false) + break + case .starGiftAttributeModel(let name, let document, let rarityPermille): + if boxed { + buffer.appendInt32(970559507) + } + serializeString(name, buffer: buffer, boxed: false) + document.serialize(buffer, true) + serializeInt32(rarityPermille, buffer: buffer, boxed: false) + break + case .starGiftAttributeOriginalDetails(let flags, let senderId, let recipientId, let date, let message): + if boxed { + buffer.appendInt32(-524291476) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {senderId!.serialize(buffer, true)} + recipientId.serialize(buffer, true) + serializeInt32(date, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)} + break + case .starGiftAttributePattern(let name, let document, let rarityPermille): + if boxed { + buffer.appendInt32(330104601) + } + serializeString(name, buffer: buffer, boxed: false) + document.serialize(buffer, true) + serializeInt32(rarityPermille, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGiftAttributeBackdrop(let name, let backdropId, let centerColor, let edgeColor, let patternColor, let textColor, let rarityPermille): + return ("starGiftAttributeBackdrop", [("name", name as Any), ("backdropId", backdropId as Any), ("centerColor", centerColor as Any), ("edgeColor", edgeColor as Any), ("patternColor", patternColor as Any), ("textColor", textColor as Any), ("rarityPermille", rarityPermille as Any)]) + case .starGiftAttributeModel(let name, let document, let rarityPermille): + return ("starGiftAttributeModel", [("name", name as Any), ("document", document as Any), ("rarityPermille", rarityPermille as Any)]) + case .starGiftAttributeOriginalDetails(let flags, let senderId, let recipientId, let date, let message): + return ("starGiftAttributeOriginalDetails", [("flags", flags as Any), ("senderId", senderId as Any), ("recipientId", recipientId as Any), ("date", date as Any), ("message", message as Any)]) + case .starGiftAttributePattern(let name, let document, let rarityPermille): + return ("starGiftAttributePattern", [("name", name as Any), ("document", document as Any), ("rarityPermille", rarityPermille as Any)]) + } + } + + public static func parse_starGiftAttributeBackdrop(_ reader: BufferReader) -> StarGiftAttribute? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + 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.StarGiftAttribute.starGiftAttributeBackdrop(name: _1!, backdropId: _2!, centerColor: _3!, edgeColor: _4!, patternColor: _5!, textColor: _6!, rarityPermille: _7!) + } + else { + return nil + } + } + public static func parse_starGiftAttributeModel(_ reader: BufferReader) -> StarGiftAttribute? { + var _1: String? + _1 = parseString(reader) + var _2: Api.Document? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Document + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.StarGiftAttribute.starGiftAttributeModel(name: _1!, document: _2!, rarityPermille: _3!) + } + else { + return nil + } + } + public static func parse_starGiftAttributeOriginalDetails(_ reader: BufferReader) -> StarGiftAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _3: Api.Peer? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _4: Int32? + _4 = reader.readInt32() + var _5: Api.TextWithEntities? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.TextWithEntities + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.StarGiftAttribute.starGiftAttributeOriginalDetails(flags: _1!, senderId: _2, recipientId: _3!, date: _4!, message: _5) + } + else { + return nil + } + } + public static func parse_starGiftAttributePattern(_ reader: BufferReader) -> StarGiftAttribute? { + var _1: String? + _1 = parseString(reader) + var _2: Api.Document? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Document + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.StarGiftAttribute.starGiftAttributePattern(name: _1!, document: _2!, rarityPermille: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StarGiftAttributeCounter: TypeConstructorDescription { + case starGiftAttributeCounter(attribute: Api.StarGiftAttributeId, count: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGiftAttributeCounter(let attribute, let count): + if boxed { + buffer.appendInt32(783398488) + } + attribute.serialize(buffer, true) + serializeInt32(count, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGiftAttributeCounter(let attribute, let count): + return ("starGiftAttributeCounter", [("attribute", attribute as Any), ("count", count as Any)]) + } + } + + public static func parse_starGiftAttributeCounter(_ reader: BufferReader) -> StarGiftAttributeCounter? { + var _1: Api.StarGiftAttributeId? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StarGiftAttributeId + } + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StarGiftAttributeCounter.starGiftAttributeCounter(attribute: _1!, count: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StarGiftAttributeId: TypeConstructorDescription { + case starGiftAttributeIdBackdrop(backdropId: Int32) + case starGiftAttributeIdModel(documentId: Int64) + case starGiftAttributeIdPattern(documentId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGiftAttributeIdBackdrop(let backdropId): + if boxed { + buffer.appendInt32(520210263) + } + serializeInt32(backdropId, buffer: buffer, boxed: false) + break + case .starGiftAttributeIdModel(let documentId): + if boxed { + buffer.appendInt32(1219145276) + } + serializeInt64(documentId, buffer: buffer, boxed: false) + break + case .starGiftAttributeIdPattern(let documentId): + if boxed { + buffer.appendInt32(1242965043) + } + serializeInt64(documentId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGiftAttributeIdBackdrop(let backdropId): + return ("starGiftAttributeIdBackdrop", [("backdropId", backdropId as Any)]) + case .starGiftAttributeIdModel(let documentId): + return ("starGiftAttributeIdModel", [("documentId", documentId as Any)]) + case .starGiftAttributeIdPattern(let documentId): + return ("starGiftAttributeIdPattern", [("documentId", documentId as Any)]) + } + } + + public static func parse_starGiftAttributeIdBackdrop(_ reader: BufferReader) -> StarGiftAttributeId? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.StarGiftAttributeId.starGiftAttributeIdBackdrop(backdropId: _1!) + } + else { + return nil + } + } + public static func parse_starGiftAttributeIdModel(_ reader: BufferReader) -> StarGiftAttributeId? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.StarGiftAttributeId.starGiftAttributeIdModel(documentId: _1!) + } + else { + return nil + } + } + public static func parse_starGiftAttributeIdPattern(_ reader: BufferReader) -> StarGiftAttributeId? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.StarGiftAttributeId.starGiftAttributeIdPattern(documentId: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum StarGiftCollection: TypeConstructorDescription { case starGiftCollection(flags: Int32, collectionId: Int32, title: String, icon: Api.Document?, giftsCount: Int32, hash: Int64) @@ -880,423 +1658,3 @@ public extension Api { } } -public extension Api { - enum StatsAbsValueAndPrev: TypeConstructorDescription { - case statsAbsValueAndPrev(current: Double, previous: Double) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsAbsValueAndPrev(let current, let previous): - if boxed { - buffer.appendInt32(-884757282) - } - serializeDouble(current, buffer: buffer, boxed: false) - serializeDouble(previous, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsAbsValueAndPrev(let current, let previous): - return ("statsAbsValueAndPrev", [("current", current as Any), ("previous", previous as Any)]) - } - } - - public static func parse_statsAbsValueAndPrev(_ reader: BufferReader) -> StatsAbsValueAndPrev? { - var _1: Double? - _1 = reader.readDouble() - var _2: Double? - _2 = reader.readDouble() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsDateRangeDays: TypeConstructorDescription { - case statsDateRangeDays(minDate: Int32, maxDate: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsDateRangeDays(let minDate, let maxDate): - if boxed { - buffer.appendInt32(-1237848657) - } - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(maxDate, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsDateRangeDays(let minDate, let maxDate): - return ("statsDateRangeDays", [("minDate", minDate as Any), ("maxDate", maxDate as Any)]) - } - } - - public static func parse_statsDateRangeDays(_ reader: BufferReader) -> StatsDateRangeDays? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsGraph: TypeConstructorDescription { - case statsGraph(flags: Int32, json: Api.DataJSON, zoomToken: String?) - case statsGraphAsync(token: String) - case statsGraphError(error: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsGraph(let flags, let json, let zoomToken): - if boxed { - buffer.appendInt32(-1901828938) - } - serializeInt32(flags, buffer: buffer, boxed: false) - json.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {serializeString(zoomToken!, buffer: buffer, boxed: false)} - break - case .statsGraphAsync(let token): - if boxed { - buffer.appendInt32(1244130093) - } - serializeString(token, buffer: buffer, boxed: false) - break - case .statsGraphError(let error): - if boxed { - buffer.appendInt32(-1092839390) - } - serializeString(error, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsGraph(let flags, let json, let zoomToken): - return ("statsGraph", [("flags", flags as Any), ("json", json as Any), ("zoomToken", zoomToken as Any)]) - case .statsGraphAsync(let token): - return ("statsGraphAsync", [("token", token as Any)]) - case .statsGraphError(let error): - return ("statsGraphError", [("error", error as Any)]) - } - } - - public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - 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.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3) - } - else { - return nil - } - } - public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.StatsGraph.statsGraphAsync(token: _1!) - } - else { - return nil - } - } - public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.StatsGraph.statsGraphError(error: _1!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsGroupTopAdmin: TypeConstructorDescription { - case statsGroupTopAdmin(userId: Int64, deleted: Int32, kicked: Int32, banned: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsGroupTopAdmin(let userId, let deleted, let kicked, let banned): - if boxed { - buffer.appendInt32(-682079097) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(deleted, buffer: buffer, boxed: false) - serializeInt32(kicked, buffer: buffer, boxed: false) - serializeInt32(banned, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsGroupTopAdmin(let userId, let deleted, let kicked, let banned): - return ("statsGroupTopAdmin", [("userId", userId as Any), ("deleted", deleted as Any), ("kicked", kicked as Any), ("banned", banned as Any)]) - } - } - - public static func parse_statsGroupTopAdmin(_ reader: BufferReader) -> StatsGroupTopAdmin? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StatsGroupTopAdmin.statsGroupTopAdmin(userId: _1!, deleted: _2!, kicked: _3!, banned: _4!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsGroupTopInviter: TypeConstructorDescription { - case statsGroupTopInviter(userId: Int64, invitations: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsGroupTopInviter(let userId, let invitations): - if boxed { - buffer.appendInt32(1398765469) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(invitations, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsGroupTopInviter(let userId, let invitations): - return ("statsGroupTopInviter", [("userId", userId as Any), ("invitations", invitations as Any)]) - } - } - - public static func parse_statsGroupTopInviter(_ reader: BufferReader) -> StatsGroupTopInviter? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsGroupTopPoster: TypeConstructorDescription { - case statsGroupTopPoster(userId: Int64, messages: Int32, avgChars: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsGroupTopPoster(let userId, let messages, let avgChars): - if boxed { - buffer.appendInt32(-1660637285) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(messages, buffer: buffer, boxed: false) - serializeInt32(avgChars, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsGroupTopPoster(let userId, let messages, let avgChars): - return ("statsGroupTopPoster", [("userId", userId as Any), ("messages", messages as Any), ("avgChars", avgChars as Any)]) - } - } - - public static func parse_statsGroupTopPoster(_ reader: BufferReader) -> StatsGroupTopPoster? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StatsGroupTopPoster.statsGroupTopPoster(userId: _1!, messages: _2!, avgChars: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsPercentValue: TypeConstructorDescription { - case statsPercentValue(part: Double, total: Double) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsPercentValue(let part, let total): - if boxed { - buffer.appendInt32(-875679776) - } - serializeDouble(part, buffer: buffer, boxed: false) - serializeDouble(total, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsPercentValue(let part, let total): - return ("statsPercentValue", [("part", part as Any), ("total", total as Any)]) - } - } - - public static func parse_statsPercentValue(_ reader: BufferReader) -> StatsPercentValue? { - var _1: Double? - _1 = reader.readDouble() - var _2: Double? - _2 = reader.readDouble() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StatsURL: TypeConstructorDescription { - case statsURL(url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .statsURL(let url): - if boxed { - buffer.appendInt32(1202287072) - } - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .statsURL(let url): - return ("statsURL", [("url", url as Any)]) - } - } - - public static func parse_statsURL(_ reader: BufferReader) -> StatsURL? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.StatsURL.statsURL(url: _1!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum StickerKeyword: TypeConstructorDescription { - case stickerKeyword(documentId: Int64, keyword: [String]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stickerKeyword(let documentId, let keyword): - if boxed { - buffer.appendInt32(-50416996) - } - serializeInt64(documentId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(keyword.count)) - for item in keyword { - serializeString(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stickerKeyword(let documentId, let keyword): - return ("stickerKeyword", [("documentId", documentId as Any), ("keyword", keyword as Any)]) - } - } - - public static func parse_stickerKeyword(_ reader: BufferReader) -> StickerKeyword? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [String]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index c2d30c953f..e0e3dbd81b 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,3 +1,423 @@ +public extension Api { + enum StatsAbsValueAndPrev: TypeConstructorDescription { + case statsAbsValueAndPrev(current: Double, previous: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsAbsValueAndPrev(let current, let previous): + if boxed { + buffer.appendInt32(-884757282) + } + serializeDouble(current, buffer: buffer, boxed: false) + serializeDouble(previous, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsAbsValueAndPrev(let current, let previous): + return ("statsAbsValueAndPrev", [("current", current as Any), ("previous", previous as Any)]) + } + } + + public static func parse_statsAbsValueAndPrev(_ reader: BufferReader) -> StatsAbsValueAndPrev? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsDateRangeDays: TypeConstructorDescription { + case statsDateRangeDays(minDate: Int32, maxDate: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsDateRangeDays(let minDate, let maxDate): + if boxed { + buffer.appendInt32(-1237848657) + } + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsDateRangeDays(let minDate, let maxDate): + return ("statsDateRangeDays", [("minDate", minDate as Any), ("maxDate", maxDate as Any)]) + } + } + + public static func parse_statsDateRangeDays(_ reader: BufferReader) -> StatsDateRangeDays? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsGraph: TypeConstructorDescription { + case statsGraph(flags: Int32, json: Api.DataJSON, zoomToken: String?) + case statsGraphAsync(token: String) + case statsGraphError(error: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsGraph(let flags, let json, let zoomToken): + if boxed { + buffer.appendInt32(-1901828938) + } + serializeInt32(flags, buffer: buffer, boxed: false) + json.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(zoomToken!, buffer: buffer, boxed: false)} + break + case .statsGraphAsync(let token): + if boxed { + buffer.appendInt32(1244130093) + } + serializeString(token, buffer: buffer, boxed: false) + break + case .statsGraphError(let error): + if boxed { + buffer.appendInt32(-1092839390) + } + serializeString(error, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsGraph(let flags, let json, let zoomToken): + return ("statsGraph", [("flags", flags as Any), ("json", json as Any), ("zoomToken", zoomToken as Any)]) + case .statsGraphAsync(let token): + return ("statsGraphAsync", [("token", token as Any)]) + case .statsGraphError(let error): + return ("statsGraphError", [("error", error as Any)]) + } + } + + public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + 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.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3) + } + else { + return nil + } + } + public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.StatsGraph.statsGraphAsync(token: _1!) + } + else { + return nil + } + } + public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.StatsGraph.statsGraphError(error: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsGroupTopAdmin: TypeConstructorDescription { + case statsGroupTopAdmin(userId: Int64, deleted: Int32, kicked: Int32, banned: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsGroupTopAdmin(let userId, let deleted, let kicked, let banned): + if boxed { + buffer.appendInt32(-682079097) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(deleted, buffer: buffer, boxed: false) + serializeInt32(kicked, buffer: buffer, boxed: false) + serializeInt32(banned, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsGroupTopAdmin(let userId, let deleted, let kicked, let banned): + return ("statsGroupTopAdmin", [("userId", userId as Any), ("deleted", deleted as Any), ("kicked", kicked as Any), ("banned", banned as Any)]) + } + } + + public static func parse_statsGroupTopAdmin(_ reader: BufferReader) -> StatsGroupTopAdmin? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.StatsGroupTopAdmin.statsGroupTopAdmin(userId: _1!, deleted: _2!, kicked: _3!, banned: _4!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsGroupTopInviter: TypeConstructorDescription { + case statsGroupTopInviter(userId: Int64, invitations: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsGroupTopInviter(let userId, let invitations): + if boxed { + buffer.appendInt32(1398765469) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(invitations, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsGroupTopInviter(let userId, let invitations): + return ("statsGroupTopInviter", [("userId", userId as Any), ("invitations", invitations as Any)]) + } + } + + public static func parse_statsGroupTopInviter(_ reader: BufferReader) -> StatsGroupTopInviter? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsGroupTopPoster: TypeConstructorDescription { + case statsGroupTopPoster(userId: Int64, messages: Int32, avgChars: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsGroupTopPoster(let userId, let messages, let avgChars): + if boxed { + buffer.appendInt32(-1660637285) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(messages, buffer: buffer, boxed: false) + serializeInt32(avgChars, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsGroupTopPoster(let userId, let messages, let avgChars): + return ("statsGroupTopPoster", [("userId", userId as Any), ("messages", messages as Any), ("avgChars", avgChars as Any)]) + } + } + + public static func parse_statsGroupTopPoster(_ reader: BufferReader) -> StatsGroupTopPoster? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.StatsGroupTopPoster.statsGroupTopPoster(userId: _1!, messages: _2!, avgChars: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsPercentValue: TypeConstructorDescription { + case statsPercentValue(part: Double, total: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsPercentValue(let part, let total): + if boxed { + buffer.appendInt32(-875679776) + } + serializeDouble(part, buffer: buffer, boxed: false) + serializeDouble(total, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsPercentValue(let part, let total): + return ("statsPercentValue", [("part", part as Any), ("total", total as Any)]) + } + } + + public static func parse_statsPercentValue(_ reader: BufferReader) -> StatsPercentValue? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StatsURL: TypeConstructorDescription { + case statsURL(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsURL(let url): + if boxed { + buffer.appendInt32(1202287072) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsURL(let url): + return ("statsURL", [("url", url as Any)]) + } + } + + public static func parse_statsURL(_ reader: BufferReader) -> StatsURL? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.StatsURL.statsURL(url: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum StickerKeyword: TypeConstructorDescription { + case stickerKeyword(documentId: Int64, keyword: [String]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stickerKeyword(let documentId, let keyword): + if boxed { + buffer.appendInt32(-50416996) + } + serializeInt64(documentId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(keyword.count)) + for item in keyword { + serializeString(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stickerKeyword(let documentId, let keyword): + return ("stickerKeyword", [("documentId", documentId as Any), ("keyword", keyword as Any)]) + } + } + + public static func parse_stickerKeyword(_ reader: BufferReader) -> StickerKeyword? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [String]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum StickerPack: TypeConstructorDescription { case stickerPack(emoticon: String, documents: [Int64]) @@ -970,493 +1390,3 @@ public extension Api { } } -public extension Api { - enum Theme: TypeConstructorDescription { - case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document?, settings: [Api.ThemeSettings]?, emoticon: String?, installsCount: Int32?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let emoticon, let installsCount): - if boxed { - buffer.appendInt32(-1609668650) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeString(slug, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(settings!.count)) - for item in settings! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 6) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt32(installsCount!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let emoticon, let installsCount): - return ("theme", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("slug", slug as Any), ("title", title as Any), ("document", document as Any), ("settings", settings as Any), ("emoticon", emoticon as Any), ("installsCount", installsCount as Any)]) - } - } - - public static func parse_theme(_ reader: BufferReader) -> Theme? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: Api.Document? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.Document - } } - var _7: [Api.ThemeSettings]? - if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ThemeSettings.self) - } } - var _8: String? - if Int(_1!) & Int(1 << 6) != 0 {_8 = parseString(reader) } - var _9: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_9 = reader.readInt32() } - 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 << 2) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 6) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6, settings: _7, emoticon: _8, installsCount: _9) - } - else { - return nil - } - } - - } -} -public extension Api { - enum ThemeSettings: TypeConstructorDescription { - case themeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.WallPaper?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper): - if boxed { - buffer.appendInt32(-94849324) - } - serializeInt32(flags, buffer: buffer, boxed: false) - baseTheme.serialize(buffer, true) - serializeInt32(accentColor, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messageColors!.count)) - for item in messageColors! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper): - return ("themeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any)]) - } - } - - public static func parse_themeSettings(_ reader: BufferReader) -> ThemeSettings? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.BaseTheme? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.BaseTheme - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() } - var _5: [Int32]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } } - var _6: Api.WallPaper? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.WallPaper - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6) - } - else { - return nil - } - } - - } -} -public extension Api { - enum Timezone: TypeConstructorDescription { - case timezone(id: String, name: String, utcOffset: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .timezone(let id, let name, let utcOffset): - if boxed { - buffer.appendInt32(-7173643) - } - serializeString(id, buffer: buffer, boxed: false) - serializeString(name, buffer: buffer, boxed: false) - serializeInt32(utcOffset, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .timezone(let id, let name, let utcOffset): - return ("timezone", [("id", id as Any), ("name", name as Any), ("utcOffset", utcOffset as Any)]) - } - } - - public static func parse_timezone(_ reader: BufferReader) -> Timezone? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Timezone.timezone(id: _1!, name: _2!, utcOffset: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum TodoCompletion: TypeConstructorDescription { - case todoCompletion(id: Int32, completedBy: Int64, date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .todoCompletion(let id, let completedBy, let date): - if boxed { - buffer.appendInt32(1287725239) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(completedBy, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .todoCompletion(let id, let completedBy, let date): - return ("todoCompletion", [("id", id as Any), ("completedBy", completedBy as Any), ("date", date as Any)]) - } - } - - public static func parse_todoCompletion(_ reader: BufferReader) -> TodoCompletion? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.TodoCompletion.todoCompletion(id: _1!, completedBy: _2!, date: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum TodoItem: TypeConstructorDescription { - case todoItem(id: Int32, title: Api.TextWithEntities) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .todoItem(let id, let title): - if boxed { - buffer.appendInt32(-878074577) - } - serializeInt32(id, buffer: buffer, boxed: false) - title.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .todoItem(let id, let title): - return ("todoItem", [("id", id as Any), ("title", title as Any)]) - } - } - - public static func parse_todoItem(_ reader: BufferReader) -> TodoItem? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.TextWithEntities? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.TextWithEntities - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TodoItem.todoItem(id: _1!, title: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum TodoList: TypeConstructorDescription { - case todoList(flags: Int32, title: Api.TextWithEntities, list: [Api.TodoItem]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .todoList(let flags, let title, let list): - if boxed { - buffer.appendInt32(1236871718) - } - serializeInt32(flags, buffer: buffer, boxed: false) - title.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(list.count)) - for item in list { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .todoList(let flags, let title, let list): - return ("todoList", [("flags", flags as Any), ("title", title as Any), ("list", list as Any)]) - } - } - - public static func parse_todoList(_ reader: BufferReader) -> TodoList? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.TextWithEntities? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.TextWithEntities - } - var _3: [Api.TodoItem]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TodoItem.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.TodoList.todoList(flags: _1!, title: _2!, list: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum TopPeer: TypeConstructorDescription { - case topPeer(peer: Api.Peer, rating: Double) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .topPeer(let peer, let rating): - if boxed { - buffer.appendInt32(-305282981) - } - peer.serialize(buffer, true) - serializeDouble(rating, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .topPeer(let peer, let rating): - return ("topPeer", [("peer", peer as Any), ("rating", rating as Any)]) - } - } - - public static func parse_topPeer(_ reader: BufferReader) -> TopPeer? { - var _1: Api.Peer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Double? - _2 = reader.readDouble() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TopPeer.topPeer(peer: _1!, rating: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum TopPeerCategory: TypeConstructorDescription { - case topPeerCategoryBotsApp - case topPeerCategoryBotsInline - case topPeerCategoryBotsPM - case topPeerCategoryChannels - case topPeerCategoryCorrespondents - case topPeerCategoryForwardChats - case topPeerCategoryForwardUsers - case topPeerCategoryGroups - case topPeerCategoryPhoneCalls - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .topPeerCategoryBotsApp: - if boxed { - buffer.appendInt32(-39945236) - } - - break - case .topPeerCategoryBotsInline: - if boxed { - buffer.appendInt32(344356834) - } - - break - case .topPeerCategoryBotsPM: - if boxed { - buffer.appendInt32(-1419371685) - } - - break - case .topPeerCategoryChannels: - if boxed { - buffer.appendInt32(371037736) - } - - break - case .topPeerCategoryCorrespondents: - if boxed { - buffer.appendInt32(104314861) - } - - break - case .topPeerCategoryForwardChats: - if boxed { - buffer.appendInt32(-68239120) - } - - break - case .topPeerCategoryForwardUsers: - if boxed { - buffer.appendInt32(-1472172887) - } - - break - case .topPeerCategoryGroups: - if boxed { - buffer.appendInt32(-1122524854) - } - - break - case .topPeerCategoryPhoneCalls: - if boxed { - buffer.appendInt32(511092620) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .topPeerCategoryBotsApp: - return ("topPeerCategoryBotsApp", []) - case .topPeerCategoryBotsInline: - return ("topPeerCategoryBotsInline", []) - case .topPeerCategoryBotsPM: - return ("topPeerCategoryBotsPM", []) - case .topPeerCategoryChannels: - return ("topPeerCategoryChannels", []) - case .topPeerCategoryCorrespondents: - return ("topPeerCategoryCorrespondents", []) - case .topPeerCategoryForwardChats: - return ("topPeerCategoryForwardChats", []) - case .topPeerCategoryForwardUsers: - return ("topPeerCategoryForwardUsers", []) - case .topPeerCategoryGroups: - return ("topPeerCategoryGroups", []) - case .topPeerCategoryPhoneCalls: - return ("topPeerCategoryPhoneCalls", []) - } - } - - public static func parse_topPeerCategoryBotsApp(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryBotsApp - } - public static func parse_topPeerCategoryBotsInline(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryBotsInline - } - public static func parse_topPeerCategoryBotsPM(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryBotsPM - } - public static func parse_topPeerCategoryChannels(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryChannels - } - public static func parse_topPeerCategoryCorrespondents(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryCorrespondents - } - public static func parse_topPeerCategoryForwardChats(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryForwardChats - } - public static func parse_topPeerCategoryForwardUsers(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryForwardUsers - } - public static func parse_topPeerCategoryGroups(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryGroups - } - public static func parse_topPeerCategoryPhoneCalls(_ reader: BufferReader) -> TopPeerCategory? { - return Api.TopPeerCategory.topPeerCategoryPhoneCalls - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 372c196121..89ce010446 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -1,3 +1,493 @@ +public extension Api { + enum Theme: TypeConstructorDescription { + case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document?, settings: [Api.ThemeSettings]?, emoticon: String?, installsCount: Int32?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let emoticon, let installsCount): + if boxed { + buffer.appendInt32(-1609668650) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeString(slug, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(settings!.count)) + for item in settings! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 6) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(installsCount!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let emoticon, let installsCount): + return ("theme", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("slug", slug as Any), ("title", title as Any), ("document", document as Any), ("settings", settings as Any), ("emoticon", emoticon as Any), ("installsCount", installsCount as Any)]) + } + } + + public static func parse_theme(_ reader: BufferReader) -> Theme? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Api.Document? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _7: [Api.ThemeSettings]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ThemeSettings.self) + } } + var _8: String? + if Int(_1!) & Int(1 << 6) != 0 {_8 = parseString(reader) } + var _9: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_9 = reader.readInt32() } + 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 << 2) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 6) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6, settings: _7, emoticon: _8, installsCount: _9) + } + else { + return nil + } + } + + } +} +public extension Api { + enum ThemeSettings: TypeConstructorDescription { + case themeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.WallPaper?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper): + if boxed { + buffer.appendInt32(-94849324) + } + serializeInt32(flags, buffer: buffer, boxed: false) + baseTheme.serialize(buffer, true) + serializeInt32(accentColor, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messageColors!.count)) + for item in messageColors! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .themeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper): + return ("themeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any)]) + } + } + + public static func parse_themeSettings(_ reader: BufferReader) -> ThemeSettings? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.BaseTheme? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BaseTheme + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() } + var _5: [Int32]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } + var _6: Api.WallPaper? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.WallPaper + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6) + } + else { + return nil + } + } + + } +} +public extension Api { + enum Timezone: TypeConstructorDescription { + case timezone(id: String, name: String, utcOffset: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .timezone(let id, let name, let utcOffset): + if boxed { + buffer.appendInt32(-7173643) + } + serializeString(id, buffer: buffer, boxed: false) + serializeString(name, buffer: buffer, boxed: false) + serializeInt32(utcOffset, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .timezone(let id, let name, let utcOffset): + return ("timezone", [("id", id as Any), ("name", name as Any), ("utcOffset", utcOffset as Any)]) + } + } + + public static func parse_timezone(_ reader: BufferReader) -> Timezone? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Timezone.timezone(id: _1!, name: _2!, utcOffset: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum TodoCompletion: TypeConstructorDescription { + case todoCompletion(id: Int32, completedBy: Int64, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .todoCompletion(let id, let completedBy, let date): + if boxed { + buffer.appendInt32(1287725239) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(completedBy, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .todoCompletion(let id, let completedBy, let date): + return ("todoCompletion", [("id", id as Any), ("completedBy", completedBy as Any), ("date", date as Any)]) + } + } + + public static func parse_todoCompletion(_ reader: BufferReader) -> TodoCompletion? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.TodoCompletion.todoCompletion(id: _1!, completedBy: _2!, date: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum TodoItem: TypeConstructorDescription { + case todoItem(id: Int32, title: Api.TextWithEntities) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .todoItem(let id, let title): + if boxed { + buffer.appendInt32(-878074577) + } + serializeInt32(id, buffer: buffer, boxed: false) + title.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .todoItem(let id, let title): + return ("todoItem", [("id", id as Any), ("title", title as Any)]) + } + } + + public static func parse_todoItem(_ reader: BufferReader) -> TodoItem? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.TextWithEntities? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.TextWithEntities + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.TodoItem.todoItem(id: _1!, title: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum TodoList: TypeConstructorDescription { + case todoList(flags: Int32, title: Api.TextWithEntities, list: [Api.TodoItem]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .todoList(let flags, let title, let list): + if boxed { + buffer.appendInt32(1236871718) + } + serializeInt32(flags, buffer: buffer, boxed: false) + title.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(list.count)) + for item in list { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .todoList(let flags, let title, let list): + return ("todoList", [("flags", flags as Any), ("title", title as Any), ("list", list as Any)]) + } + } + + public static func parse_todoList(_ reader: BufferReader) -> TodoList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.TextWithEntities? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.TextWithEntities + } + var _3: [Api.TodoItem]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TodoItem.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.TodoList.todoList(flags: _1!, title: _2!, list: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum TopPeer: TypeConstructorDescription { + case topPeer(peer: Api.Peer, rating: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .topPeer(let peer, let rating): + if boxed { + buffer.appendInt32(-305282981) + } + peer.serialize(buffer, true) + serializeDouble(rating, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .topPeer(let peer, let rating): + return ("topPeer", [("peer", peer as Any), ("rating", rating as Any)]) + } + } + + public static func parse_topPeer(_ reader: BufferReader) -> TopPeer? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.TopPeer.topPeer(peer: _1!, rating: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum TopPeerCategory: TypeConstructorDescription { + case topPeerCategoryBotsApp + case topPeerCategoryBotsInline + case topPeerCategoryBotsPM + case topPeerCategoryChannels + case topPeerCategoryCorrespondents + case topPeerCategoryForwardChats + case topPeerCategoryForwardUsers + case topPeerCategoryGroups + case topPeerCategoryPhoneCalls + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .topPeerCategoryBotsApp: + if boxed { + buffer.appendInt32(-39945236) + } + + break + case .topPeerCategoryBotsInline: + if boxed { + buffer.appendInt32(344356834) + } + + break + case .topPeerCategoryBotsPM: + if boxed { + buffer.appendInt32(-1419371685) + } + + break + case .topPeerCategoryChannels: + if boxed { + buffer.appendInt32(371037736) + } + + break + case .topPeerCategoryCorrespondents: + if boxed { + buffer.appendInt32(104314861) + } + + break + case .topPeerCategoryForwardChats: + if boxed { + buffer.appendInt32(-68239120) + } + + break + case .topPeerCategoryForwardUsers: + if boxed { + buffer.appendInt32(-1472172887) + } + + break + case .topPeerCategoryGroups: + if boxed { + buffer.appendInt32(-1122524854) + } + + break + case .topPeerCategoryPhoneCalls: + if boxed { + buffer.appendInt32(511092620) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .topPeerCategoryBotsApp: + return ("topPeerCategoryBotsApp", []) + case .topPeerCategoryBotsInline: + return ("topPeerCategoryBotsInline", []) + case .topPeerCategoryBotsPM: + return ("topPeerCategoryBotsPM", []) + case .topPeerCategoryChannels: + return ("topPeerCategoryChannels", []) + case .topPeerCategoryCorrespondents: + return ("topPeerCategoryCorrespondents", []) + case .topPeerCategoryForwardChats: + return ("topPeerCategoryForwardChats", []) + case .topPeerCategoryForwardUsers: + return ("topPeerCategoryForwardUsers", []) + case .topPeerCategoryGroups: + return ("topPeerCategoryGroups", []) + case .topPeerCategoryPhoneCalls: + return ("topPeerCategoryPhoneCalls", []) + } + } + + public static func parse_topPeerCategoryBotsApp(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryBotsApp + } + public static func parse_topPeerCategoryBotsInline(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryBotsInline + } + public static func parse_topPeerCategoryBotsPM(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryBotsPM + } + public static func parse_topPeerCategoryChannels(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryChannels + } + public static func parse_topPeerCategoryCorrespondents(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryCorrespondents + } + public static func parse_topPeerCategoryForwardChats(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryForwardChats + } + public static func parse_topPeerCategoryForwardUsers(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryForwardUsers + } + public static func parse_topPeerCategoryGroups(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryGroups + } + public static func parse_topPeerCategoryPhoneCalls(_ reader: BufferReader) -> TopPeerCategory? { + return Api.TopPeerCategory.topPeerCategoryPhoneCalls + } + + } +} public extension Api { enum TopPeerCategoryPeers: TypeConstructorDescription { case topPeerCategoryPeers(category: Api.TopPeerCategory, count: Int32, peers: [Api.TopPeer]) diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index ad1981f70f..49b9046b8c 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -614,13 +614,13 @@ public extension Api { } public extension Api { enum UserFull: TypeConstructorDescription { - case userFull(flags: Int32, flags2: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?, businessWorkHours: Api.BusinessWorkHours?, businessLocation: Api.BusinessLocation?, businessGreetingMessage: Api.BusinessGreetingMessage?, businessAwayMessage: Api.BusinessAwayMessage?, businessIntro: Api.BusinessIntro?, birthday: Api.Birthday?, personalChannelId: Int64?, personalChannelMessage: Int32?, stargiftsCount: Int32?, starrefProgram: Api.StarRefProgram?, botVerification: Api.BotVerification?, sendPaidMessagesStars: Int64?, disallowedGifts: Api.DisallowedGiftsSettings?, starsRating: Api.StarsRating?, starsMyPendingRating: Api.StarsRating?, starsMyPendingRatingDate: Int32?) + case userFull(flags: Int32, flags2: Int32, id: Int64, about: String?, settings: Api.PeerSettings, personalPhoto: Api.Photo?, profilePhoto: Api.Photo?, fallbackPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, theme: Api.ChatTheme?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?, wallpaper: Api.WallPaper?, stories: Api.PeerStories?, businessWorkHours: Api.BusinessWorkHours?, businessLocation: Api.BusinessLocation?, businessGreetingMessage: Api.BusinessGreetingMessage?, businessAwayMessage: Api.BusinessAwayMessage?, businessIntro: Api.BusinessIntro?, birthday: Api.Birthday?, personalChannelId: Int64?, personalChannelMessage: Int32?, stargiftsCount: Int32?, starrefProgram: Api.StarRefProgram?, botVerification: Api.BotVerification?, sendPaidMessagesStars: Int64?, disallowedGifts: Api.DisallowedGiftsSettings?, starsRating: Api.StarsRating?, starsMyPendingRating: Api.StarsRating?, starsMyPendingRatingDate: Int32?, mainTab: Api.ProfileTab?, savedMusic: Api.Document?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts, let starsRating, let starsMyPendingRating, let starsMyPendingRatingDate): + case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let theme, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts, let starsRating, let starsMyPendingRating, let starsMyPendingRatingDate, let mainTab, let savedMusic): if boxed { - buffer.appendInt32(2120470047) + buffer.appendInt32(-982010451) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -636,7 +636,7 @@ public extension Api { serializeInt32(commonChatsCount, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 15) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 15) != 0 {theme!.serialize(buffer, true)} if Int(flags) & Int(1 << 16) != 0 {serializeString(privateForwardName!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 17) != 0 {botGroupAdminRights!.serialize(buffer, true)} if Int(flags) & Int(1 << 18) != 0 {botBroadcastAdminRights!.serialize(buffer, true)} @@ -658,14 +658,16 @@ public extension Api { if Int(flags2) & Int(1 << 17) != 0 {starsRating!.serialize(buffer, true)} if Int(flags2) & Int(1 << 18) != 0 {starsMyPendingRating!.serialize(buffer, true)} if Int(flags2) & Int(1 << 18) != 0 {serializeInt32(starsMyPendingRatingDate!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 20) != 0 {mainTab!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 21) != 0 {savedMusic!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts, let starsRating, let starsMyPendingRating, let starsMyPendingRatingDate): - return ("userFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("themeEmoticon", themeEmoticon as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any), ("businessWorkHours", businessWorkHours as Any), ("businessLocation", businessLocation as Any), ("businessGreetingMessage", businessGreetingMessage as Any), ("businessAwayMessage", businessAwayMessage as Any), ("businessIntro", businessIntro as Any), ("birthday", birthday as Any), ("personalChannelId", personalChannelId as Any), ("personalChannelMessage", personalChannelMessage as Any), ("stargiftsCount", stargiftsCount as Any), ("starrefProgram", starrefProgram as Any), ("botVerification", botVerification as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any), ("disallowedGifts", disallowedGifts as Any), ("starsRating", starsRating as Any), ("starsMyPendingRating", starsMyPendingRating as Any), ("starsMyPendingRatingDate", starsMyPendingRatingDate as Any)]) + case .userFull(let flags, let flags2, let id, let about, let settings, let personalPhoto, let profilePhoto, let fallbackPhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let theme, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights, let wallpaper, let stories, let businessWorkHours, let businessLocation, let businessGreetingMessage, let businessAwayMessage, let businessIntro, let birthday, let personalChannelId, let personalChannelMessage, let stargiftsCount, let starrefProgram, let botVerification, let sendPaidMessagesStars, let disallowedGifts, let starsRating, let starsMyPendingRating, let starsMyPendingRatingDate, let mainTab, let savedMusic): + return ("userFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("settings", settings as Any), ("personalPhoto", personalPhoto as Any), ("profilePhoto", profilePhoto as Any), ("fallbackPhoto", fallbackPhoto as Any), ("notifySettings", notifySettings as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("commonChatsCount", commonChatsCount as Any), ("folderId", folderId as Any), ("ttlPeriod", ttlPeriod as Any), ("theme", theme as Any), ("privateForwardName", privateForwardName as Any), ("botGroupAdminRights", botGroupAdminRights as Any), ("botBroadcastAdminRights", botBroadcastAdminRights as Any), ("wallpaper", wallpaper as Any), ("stories", stories as Any), ("businessWorkHours", businessWorkHours as Any), ("businessLocation", businessLocation as Any), ("businessGreetingMessage", businessGreetingMessage as Any), ("businessAwayMessage", businessAwayMessage as Any), ("businessIntro", businessIntro as Any), ("birthday", birthday as Any), ("personalChannelId", personalChannelId as Any), ("personalChannelMessage", personalChannelMessage as Any), ("stargiftsCount", stargiftsCount as Any), ("starrefProgram", starrefProgram as Any), ("botVerification", botVerification as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any), ("disallowedGifts", disallowedGifts as Any), ("starsRating", starsRating as Any), ("starsMyPendingRating", starsMyPendingRating as Any), ("starsMyPendingRatingDate", starsMyPendingRatingDate as Any), ("mainTab", mainTab as Any), ("savedMusic", savedMusic as Any)]) } } @@ -710,8 +712,10 @@ public extension Api { if Int(_1!) & Int(1 << 11) != 0 {_13 = reader.readInt32() } var _14: Int32? if Int(_1!) & Int(1 << 14) != 0 {_14 = reader.readInt32() } - var _15: String? - if Int(_1!) & Int(1 << 15) != 0 {_15 = parseString(reader) } + var _15: Api.ChatTheme? + if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { + _15 = Api.parse(reader, signature: signature) as? Api.ChatTheme + } } var _16: String? if Int(_1!) & Int(1 << 16) != 0 {_16 = parseString(reader) } var _17: Api.ChatAdminRights? @@ -784,6 +788,14 @@ public extension Api { } } var _36: Int32? if Int(_2!) & Int(1 << 18) != 0 {_36 = reader.readInt32() } + var _37: Api.ProfileTab? + if Int(_2!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() { + _37 = Api.parse(reader, signature: signature) as? Api.ProfileTab + } } + var _38: Api.Document? + if Int(_2!) & Int(1 << 21) != 0 {if let signature = reader.readInt32() { + _38 = Api.parse(reader, signature: signature) as? Api.Document + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -820,8 +832,10 @@ public extension Api { let _c34 = (Int(_2!) & Int(1 << 17) == 0) || _34 != nil let _c35 = (Int(_2!) & Int(1 << 18) == 0) || _35 != nil let _c36 = (Int(_2!) & Int(1 << 18) == 0) || _36 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 { - return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, themeEmoticon: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedGifts: _33, starsRating: _34, starsMyPendingRating: _35, starsMyPendingRatingDate: _36) + let _c37 = (Int(_2!) & Int(1 << 20) == 0) || _37 != nil + let _c38 = (Int(_2!) & Int(1 << 21) == 0) || _38 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 { + return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, theme: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedGifts: _33, starsRating: _34, starsMyPendingRating: _35, starsMyPendingRatingDate: _36, mainTab: _37, savedMusic: _38) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index cc6313ea7d..b2a68ca7b1 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -568,6 +568,92 @@ public extension Api.account { } } +public extension Api.account { + enum ChatThemes: TypeConstructorDescription { + case chatThemes(flags: Int32, hash: Int64, themes: [Api.ChatTheme], chats: [Api.Chat], users: [Api.User], nextOffset: Int32?) + case chatThemesNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatThemes(let flags, let hash, let themes, let chats, let users, let nextOffset): + if boxed { + buffer.appendInt32(373835863) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(themes.count)) + for item in themes { + 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 {serializeInt32(nextOffset!, buffer: buffer, boxed: false)} + break + case .chatThemesNotModified: + if boxed { + buffer.appendInt32(-535699004) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatThemes(let flags, let hash, let themes, let chats, let users, let nextOffset): + return ("chatThemes", [("flags", flags as Any), ("hash", hash as Any), ("themes", themes as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + case .chatThemesNotModified: + return ("chatThemesNotModified", []) + } + } + + public static func parse_chatThemes(_ reader: BufferReader) -> ChatThemes? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: [Api.ChatTheme]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatTheme.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: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } + 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.account.ChatThemes.chatThemes(flags: _1!, hash: _2!, themes: _3!, chats: _4!, users: _5!, nextOffset: _6) + } + else { + return nil + } + } + public static func parse_chatThemesNotModified(_ reader: BufferReader) -> ChatThemes? { + return Api.account.ChatThemes.chatThemesNotModified + } + + } +} public extension Api.account { enum ConnectedBots: TypeConstructorDescription { case connectedBots(connectedBots: [Api.ConnectedBot], users: [Api.User]) @@ -1206,6 +1292,60 @@ public extension Api.account { } } +public extension Api.account { + enum SavedMusicIds: TypeConstructorDescription { + case savedMusicIds(ids: [Int64]) + case savedMusicIdsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedMusicIds(let ids): + if boxed { + buffer.appendInt32(-1718786506) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(ids.count)) + for item in ids { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .savedMusicIdsNotModified: + if boxed { + buffer.appendInt32(1338514798) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedMusicIds(let ids): + return ("savedMusicIds", [("ids", ids as Any)]) + case .savedMusicIdsNotModified: + return ("savedMusicIdsNotModified", []) + } + } + + public static func parse_savedMusicIds(_ reader: BufferReader) -> SavedMusicIds? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.account.SavedMusicIds.savedMusicIds(ids: _1!) + } + else { + return nil + } + } + public static func parse_savedMusicIdsNotModified(_ reader: BufferReader) -> SavedMusicIds? { + return Api.account.SavedMusicIds.savedMusicIdsNotModified + } + + } +} public extension Api.account { enum SavedRingtone: TypeConstructorDescription { case savedRingtone @@ -1256,101 +1396,3 @@ public extension Api.account { } } -public extension Api.account { - enum SavedRingtones: TypeConstructorDescription { - case savedRingtones(hash: Int64, ringtones: [Api.Document]) - case savedRingtonesNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .savedRingtones(let hash, let ringtones): - if boxed { - buffer.appendInt32(-1041683259) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(ringtones.count)) - for item in ringtones { - item.serialize(buffer, true) - } - break - case .savedRingtonesNotModified: - if boxed { - buffer.appendInt32(-67704655) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedRingtones(let hash, let ringtones): - return ("savedRingtones", [("hash", hash as Any), ("ringtones", ringtones as Any)]) - case .savedRingtonesNotModified: - return ("savedRingtonesNotModified", []) - } - } - - public static func parse_savedRingtones(_ reader: BufferReader) -> SavedRingtones? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Document]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) - } - else { - return nil - } - } - public static func parse_savedRingtonesNotModified(_ reader: BufferReader) -> SavedRingtones? { - return Api.account.SavedRingtones.savedRingtonesNotModified - } - - } -} -public extension Api.account { - enum SentEmailCode: TypeConstructorDescription { - case sentEmailCode(emailPattern: String, length: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sentEmailCode(let emailPattern, let length): - if boxed { - buffer.appendInt32(-2128640689) - } - serializeString(emailPattern, buffer: buffer, boxed: false) - serializeInt32(length, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sentEmailCode(let emailPattern, let length): - return ("sentEmailCode", [("emailPattern", emailPattern as Any), ("length", length as Any)]) - } - } - - public static func parse_sentEmailCode(_ reader: BufferReader) -> SentEmailCode? { - var _1: String? - _1 = parseString(reader) - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 0d3383bf64..cd4d6eb155 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1,3 +1,101 @@ +public extension Api.account { + enum SavedRingtones: TypeConstructorDescription { + case savedRingtones(hash: Int64, ringtones: [Api.Document]) + case savedRingtonesNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedRingtones(let hash, let ringtones): + if boxed { + buffer.appendInt32(-1041683259) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(ringtones.count)) + for item in ringtones { + item.serialize(buffer, true) + } + break + case .savedRingtonesNotModified: + if boxed { + buffer.appendInt32(-67704655) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedRingtones(let hash, let ringtones): + return ("savedRingtones", [("hash", hash as Any), ("ringtones", ringtones as Any)]) + case .savedRingtonesNotModified: + return ("savedRingtonesNotModified", []) + } + } + + public static func parse_savedRingtones(_ reader: BufferReader) -> SavedRingtones? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Document]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) + } + else { + return nil + } + } + public static func parse_savedRingtonesNotModified(_ reader: BufferReader) -> SavedRingtones? { + return Api.account.SavedRingtones.savedRingtonesNotModified + } + + } +} +public extension Api.account { + enum SentEmailCode: TypeConstructorDescription { + case sentEmailCode(emailPattern: String, length: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sentEmailCode(let emailPattern, let length): + if boxed { + buffer.appendInt32(-2128640689) + } + serializeString(emailPattern, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sentEmailCode(let emailPattern, let length): + return ("sentEmailCode", [("emailPattern", emailPattern as Any), ("length", length as Any)]) + } + } + + public static func parse_sentEmailCode(_ reader: BufferReader) -> SentEmailCode? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) + } + else { + return nil + } + } + + } +} public extension Api.account { enum Takeout: TypeConstructorDescription { case takeout(id: Int64) @@ -603,7 +701,7 @@ public extension Api.auth { public extension Api.auth { enum SentCode: TypeConstructorDescription { case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?) - case sentCodePaymentRequired(storeProduct: String, phoneCodeHash: String) + case sentCodePaymentRequired(storeProduct: String, phoneCodeHash: String, supportEmailAddress: String, supportEmailSubject: String) case sentCodeSuccess(authorization: Api.auth.Authorization) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { @@ -618,12 +716,14 @@ public extension Api.auth { if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} break - case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash): + case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash, let supportEmailAddress, let supportEmailSubject): if boxed { - buffer.appendInt32(-674301568) + buffer.appendInt32(-677184263) } serializeString(storeProduct, buffer: buffer, boxed: false) serializeString(phoneCodeHash, buffer: buffer, boxed: false) + serializeString(supportEmailAddress, buffer: buffer, boxed: false) + serializeString(supportEmailSubject, buffer: buffer, boxed: false) break case .sentCodeSuccess(let authorization): if boxed { @@ -638,8 +738,8 @@ public extension Api.auth { switch self { case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout): return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)]) - case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash): - return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any), ("phoneCodeHash", phoneCodeHash as Any)]) + case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash, let supportEmailAddress, let supportEmailSubject): + return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any), ("phoneCodeHash", phoneCodeHash as Any), ("supportEmailAddress", supportEmailAddress as Any), ("supportEmailSubject", supportEmailSubject as Any)]) case .sentCodeSuccess(let authorization): return ("sentCodeSuccess", [("authorization", authorization as Any)]) } @@ -677,10 +777,16 @@ public extension Api.auth { _1 = parseString(reader) var _2: String? _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!, supportEmailAddress: _3!, supportEmailSubject: _4!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api35.swift b/submodules/TelegramApi/Sources/Api35.swift index 1c8af79156..9d5745780a 100644 --- a/submodules/TelegramApi/Sources/Api35.swift +++ b/submodules/TelegramApi/Sources/Api35.swift @@ -812,16 +812,21 @@ public extension Api.messages { } public extension Api.messages { indirect enum WebPagePreview: TypeConstructorDescription { - case webPagePreview(media: Api.MessageMedia, users: [Api.User]) + case webPagePreview(media: Api.MessageMedia, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .webPagePreview(let media, let users): + case .webPagePreview(let media, let chats, let users): if boxed { - buffer.appendInt32(-1254192351) + buffer.appendInt32(-1936029524) } media.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) @@ -832,8 +837,8 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .webPagePreview(let media, let users): - return ("webPagePreview", [("media", media as Any), ("users", users as Any)]) + case .webPagePreview(let media, let chats, let users): + return ("webPagePreview", [("media", media as Any), ("chats", chats as Any), ("users", users as Any)]) } } @@ -842,14 +847,19 @@ public extension Api.messages { if let signature = reader.readInt32() { _1 = Api.parse(reader, signature: signature) as? Api.MessageMedia } - var _2: [Api.User]? + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _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 - if _c1 && _c2 { - return Api.messages.WebPagePreview.webPagePreview(media: _1!, users: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.WebPagePreview.webPagePreview(media: _1!, chats: _2!, users: _3!) } else { return nil @@ -904,6 +914,56 @@ public extension Api.payments { } } +public extension Api.payments { + enum CheckCanSendGiftResult: TypeConstructorDescription { + case checkCanSendGiftResultFail(reason: Api.TextWithEntities) + case checkCanSendGiftResultOk + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .checkCanSendGiftResultFail(let reason): + if boxed { + buffer.appendInt32(-706379148) + } + reason.serialize(buffer, true) + break + case .checkCanSendGiftResultOk: + if boxed { + buffer.appendInt32(927967149) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .checkCanSendGiftResultFail(let reason): + return ("checkCanSendGiftResultFail", [("reason", reason as Any)]) + case .checkCanSendGiftResultOk: + return ("checkCanSendGiftResultOk", []) + } + } + + public static func parse_checkCanSendGiftResultFail(_ reader: BufferReader) -> CheckCanSendGiftResult? { + var _1: Api.TextWithEntities? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.TextWithEntities + } + let _c1 = _1 != nil + if _c1 { + return Api.payments.CheckCanSendGiftResult.checkCanSendGiftResultFail(reason: _1!) + } + else { + return nil + } + } + public static func parse_checkCanSendGiftResultOk(_ reader: BufferReader) -> CheckCanSendGiftResult? { + return Api.payments.CheckCanSendGiftResult.checkCanSendGiftResultOk + } + + } +} public extension Api.payments { enum CheckedGiftCode: TypeConstructorDescription { case checkedGiftCode(flags: Int32, fromId: Api.Peer?, giveawayMsgId: Int32?, toId: Int64?, date: Int32, months: Int32, usedDate: Int32?, chats: [Api.Chat], users: [Api.User]) @@ -1552,61 +1612,3 @@ public extension Api.payments { } } -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 - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api36.swift b/submodules/TelegramApi/Sources/Api36.swift index 64ef1503e5..e81a788632 100644 --- a/submodules/TelegramApi/Sources/Api36.swift +++ b/submodules/TelegramApi/Sources/Api36.swift @@ -1,3 +1,61 @@ +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 ResaleStarGifts: TypeConstructorDescription { case resaleStarGifts(flags: Int32, count: Int32, gifts: [Api.StarGift], nextOffset: String?, attributes: [Api.StarGiftAttribute]?, attributesHash: Int64?, chats: [Api.Chat], counters: [Api.StarGiftAttributeCounter]?, users: [Api.User]) @@ -718,16 +776,21 @@ public extension Api.payments { } public extension Api.payments { enum UniqueStarGift: TypeConstructorDescription { - case uniqueStarGift(gift: Api.StarGift, users: [Api.User]) + case uniqueStarGift(gift: Api.StarGift, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .uniqueStarGift(let gift, let users): + case .uniqueStarGift(let gift, let chats, let users): if boxed { - buffer.appendInt32(-895289845) + buffer.appendInt32(1097619176) } gift.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) @@ -738,8 +801,8 @@ public extension Api.payments { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .uniqueStarGift(let gift, let users): - return ("uniqueStarGift", [("gift", gift as Any), ("users", users as Any)]) + case .uniqueStarGift(let gift, let chats, let users): + return ("uniqueStarGift", [("gift", gift as Any), ("chats", chats as Any), ("users", users as Any)]) } } @@ -748,14 +811,103 @@ public extension Api.payments { if let signature = reader.readInt32() { _1 = Api.parse(reader, signature: signature) as? Api.StarGift } - var _2: [Api.User]? + var _2: [Api.Chat]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _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 - if _c1 && _c2 { - return Api.payments.UniqueStarGift.uniqueStarGift(gift: _1!, users: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.payments.UniqueStarGift.uniqueStarGift(gift: _1!, chats: _2!, users: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum UniqueStarGiftValueInfo: TypeConstructorDescription { + case uniqueStarGiftValueInfo(flags: Int32, currency: String, value: Int64, initialSaleDate: Int32, initialSaleStars: Int64, initialSalePrice: Int64, lastSaleDate: Int32?, lastSalePrice: Int64?, floorPrice: Int64?, averagePrice: Int64?, listedCount: Int32?, fragmentListedCount: Int32?, fragmentListedUrl: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .uniqueStarGiftValueInfo(let flags, let currency, let value, let initialSaleDate, let initialSaleStars, let initialSalePrice, let lastSaleDate, let lastSalePrice, let floorPrice, let averagePrice, let listedCount, let fragmentListedCount, let fragmentListedUrl): + if boxed { + buffer.appendInt32(1362093126) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(value, buffer: buffer, boxed: false) + serializeInt32(initialSaleDate, buffer: buffer, boxed: false) + serializeInt64(initialSaleStars, buffer: buffer, boxed: false) + serializeInt64(initialSalePrice, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(lastSaleDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(lastSalePrice!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt64(floorPrice!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(averagePrice!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(listedCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(fragmentListedCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeString(fragmentListedUrl!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .uniqueStarGiftValueInfo(let flags, let currency, let value, let initialSaleDate, let initialSaleStars, let initialSalePrice, let lastSaleDate, let lastSalePrice, let floorPrice, let averagePrice, let listedCount, let fragmentListedCount, let fragmentListedUrl): + return ("uniqueStarGiftValueInfo", [("flags", flags as Any), ("currency", currency as Any), ("value", value as Any), ("initialSaleDate", initialSaleDate as Any), ("initialSaleStars", initialSaleStars as Any), ("initialSalePrice", initialSalePrice as Any), ("lastSaleDate", lastSaleDate as Any), ("lastSalePrice", lastSalePrice as Any), ("floorPrice", floorPrice as Any), ("averagePrice", averagePrice as Any), ("listedCount", listedCount as Any), ("fragmentListedCount", fragmentListedCount as Any), ("fragmentListedUrl", fragmentListedUrl as Any)]) + } + } + + public static func parse_uniqueStarGiftValueInfo(_ reader: BufferReader) -> UniqueStarGiftValueInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int64? + _5 = reader.readInt64() + var _6: Int64? + _6 = reader.readInt64() + var _7: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_7 = reader.readInt32() } + var _8: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt64() } + var _9: Int64? + if Int(_1!) & Int(1 << 2) != 0 {_9 = reader.readInt64() } + var _10: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_10 = reader.readInt64() } + var _11: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_11 = reader.readInt32() } + var _12: Int32? + if Int(_1!) & Int(1 << 5) != 0 {_12 = reader.readInt32() } + var _13: String? + if Int(_1!) & Int(1 << 5) != 0 {_13 = parseString(reader) } + 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 << 0) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 3) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 5) == 0) || _13 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { + return Api.payments.UniqueStarGiftValueInfo.uniqueStarGiftValueInfo(flags: _1!, currency: _2!, value: _3!, initialSaleDate: _4!, initialSaleStars: _5!, initialSalePrice: _6!, lastSaleDate: _7, lastSalePrice: _8, floorPrice: _9, averagePrice: _10, listedCount: _11, fragmentListedCount: _12, fragmentListedUrl: _13) } else { return nil @@ -1540,275 +1692,3 @@ public extension Api.premium { } } -public extension Api.smsjobs { - enum EligibilityToJoin: TypeConstructorDescription { - case eligibleToJoin(termsUrl: String, monthlySentSms: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .eligibleToJoin(let termsUrl, let monthlySentSms): - if boxed { - buffer.appendInt32(-594852657) - } - serializeString(termsUrl, buffer: buffer, boxed: false) - serializeInt32(monthlySentSms, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .eligibleToJoin(let termsUrl, let monthlySentSms): - return ("eligibleToJoin", [("termsUrl", termsUrl as Any), ("monthlySentSms", monthlySentSms as Any)]) - } - } - - public static func parse_eligibleToJoin(_ reader: BufferReader) -> EligibilityToJoin? { - var _1: String? - _1 = parseString(reader) - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.smsjobs.EligibilityToJoin.eligibleToJoin(termsUrl: _1!, monthlySentSms: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.smsjobs { - enum Status: TypeConstructorDescription { - case status(flags: Int32, recentSent: Int32, recentSince: Int32, recentRemains: Int32, totalSent: Int32, totalSince: Int32, lastGiftSlug: String?, termsUrl: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .status(let flags, let recentSent, let recentSince, let recentRemains, let totalSent, let totalSince, let lastGiftSlug, let termsUrl): - if boxed { - buffer.appendInt32(720277905) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(recentSent, buffer: buffer, boxed: false) - serializeInt32(recentSince, buffer: buffer, boxed: false) - serializeInt32(recentRemains, buffer: buffer, boxed: false) - serializeInt32(totalSent, buffer: buffer, boxed: false) - serializeInt32(totalSince, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(lastGiftSlug!, buffer: buffer, boxed: false)} - serializeString(termsUrl, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .status(let flags, let recentSent, let recentSince, let recentRemains, let totalSent, let totalSince, let lastGiftSlug, let termsUrl): - return ("status", [("flags", flags as Any), ("recentSent", recentSent as Any), ("recentSince", recentSince as Any), ("recentRemains", recentRemains as Any), ("totalSent", totalSent as Any), ("totalSince", totalSince as Any), ("lastGiftSlug", lastGiftSlug as Any), ("termsUrl", termsUrl as Any)]) - } - } - - public static func parse_status(_ reader: BufferReader) -> Status? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Int32? - _6 = reader.readInt32() - var _7: String? - if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } - var _8: String? - _8 = parseString(reader) - 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 << 1) == 0) || _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.smsjobs.Status.status(flags: _1!, recentSent: _2!, recentSince: _3!, recentRemains: _4!, totalSent: _5!, totalSince: _6!, lastGiftSlug: _7, termsUrl: _8!) - } - else { - return nil - } - } - - } -} -public extension Api.stats { - enum BroadcastStats: TypeConstructorDescription { - case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, reactionsPerPost: Api.StatsAbsValueAndPrev, viewsPerStory: Api.StatsAbsValueAndPrev, sharesPerStory: Api.StatsAbsValueAndPrev, reactionsPerStory: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph, ivInteractionsGraph: Api.StatsGraph, viewsBySourceGraph: Api.StatsGraph, newFollowersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph, storyInteractionsGraph: Api.StatsGraph, storyReactionsByEmotionGraph: Api.StatsGraph, recentPostsInteractions: [Api.PostInteractionCounters]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let reactionsPerPost, let viewsPerStory, let sharesPerStory, let reactionsPerStory, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let reactionsByEmotionGraph, let storyInteractionsGraph, let storyReactionsByEmotionGraph, let recentPostsInteractions): - if boxed { - buffer.appendInt32(963421692) - } - period.serialize(buffer, true) - followers.serialize(buffer, true) - viewsPerPost.serialize(buffer, true) - sharesPerPost.serialize(buffer, true) - reactionsPerPost.serialize(buffer, true) - viewsPerStory.serialize(buffer, true) - sharesPerStory.serialize(buffer, true) - reactionsPerStory.serialize(buffer, true) - enabledNotifications.serialize(buffer, true) - growthGraph.serialize(buffer, true) - followersGraph.serialize(buffer, true) - muteGraph.serialize(buffer, true) - topHoursGraph.serialize(buffer, true) - interactionsGraph.serialize(buffer, true) - ivInteractionsGraph.serialize(buffer, true) - viewsBySourceGraph.serialize(buffer, true) - newFollowersBySourceGraph.serialize(buffer, true) - languagesGraph.serialize(buffer, true) - reactionsByEmotionGraph.serialize(buffer, true) - storyInteractionsGraph.serialize(buffer, true) - storyReactionsByEmotionGraph.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(recentPostsInteractions.count)) - for item in recentPostsInteractions { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let reactionsPerPost, let viewsPerStory, let sharesPerStory, let reactionsPerStory, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let reactionsByEmotionGraph, let storyInteractionsGraph, let storyReactionsByEmotionGraph, let recentPostsInteractions): - return ("broadcastStats", [("period", period as Any), ("followers", followers as Any), ("viewsPerPost", viewsPerPost as Any), ("sharesPerPost", sharesPerPost as Any), ("reactionsPerPost", reactionsPerPost as Any), ("viewsPerStory", viewsPerStory as Any), ("sharesPerStory", sharesPerStory as Any), ("reactionsPerStory", reactionsPerStory as Any), ("enabledNotifications", enabledNotifications as Any), ("growthGraph", growthGraph as Any), ("followersGraph", followersGraph as Any), ("muteGraph", muteGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("interactionsGraph", interactionsGraph as Any), ("ivInteractionsGraph", ivInteractionsGraph as Any), ("viewsBySourceGraph", viewsBySourceGraph as Any), ("newFollowersBySourceGraph", newFollowersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any), ("storyInteractionsGraph", storyInteractionsGraph as Any), ("storyReactionsByEmotionGraph", storyReactionsByEmotionGraph as Any), ("recentPostsInteractions", recentPostsInteractions as Any)]) - } - } - - public static func parse_broadcastStats(_ reader: BufferReader) -> BroadcastStats? { - var _1: Api.StatsDateRangeDays? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays - } - var _2: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _3: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _4: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _5: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _6: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _7: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _8: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _9: Api.StatsPercentValue? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue - } - var _10: Api.StatsGraph? - if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _11: Api.StatsGraph? - if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _12: Api.StatsGraph? - if let signature = reader.readInt32() { - _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _13: Api.StatsGraph? - if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _14: Api.StatsGraph? - if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _15: Api.StatsGraph? - if let signature = reader.readInt32() { - _15 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _16: Api.StatsGraph? - if let signature = reader.readInt32() { - _16 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _17: Api.StatsGraph? - if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _18: Api.StatsGraph? - if let signature = reader.readInt32() { - _18 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _19: Api.StatsGraph? - if let signature = reader.readInt32() { - _19 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _20: Api.StatsGraph? - if let signature = reader.readInt32() { - _20 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _21: Api.StatsGraph? - if let signature = reader.readInt32() { - _21 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _22: [Api.PostInteractionCounters]? - if let _ = reader.readInt32() { - _22 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PostInteractionCounters.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 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - let _c16 = _16 != nil - let _c17 = _17 != nil - let _c18 = _18 != nil - let _c19 = _19 != nil - let _c20 = _20 != nil - let _c21 = _21 != nil - let _c22 = _22 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { - return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, reactionsPerPost: _5!, viewsPerStory: _6!, sharesPerStory: _7!, reactionsPerStory: _8!, enabledNotifications: _9!, growthGraph: _10!, followersGraph: _11!, muteGraph: _12!, topHoursGraph: _13!, interactionsGraph: _14!, ivInteractionsGraph: _15!, viewsBySourceGraph: _16!, newFollowersBySourceGraph: _17!, languagesGraph: _18!, reactionsByEmotionGraph: _19!, storyInteractionsGraph: _20!, storyReactionsByEmotionGraph: _21!, recentPostsInteractions: _22!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api37.swift b/submodules/TelegramApi/Sources/Api37.swift index cbba07a113..324c1440e2 100644 --- a/submodules/TelegramApi/Sources/Api37.swift +++ b/submodules/TelegramApi/Sources/Api37.swift @@ -1,3 +1,275 @@ +public extension Api.smsjobs { + enum EligibilityToJoin: TypeConstructorDescription { + case eligibleToJoin(termsUrl: String, monthlySentSms: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .eligibleToJoin(let termsUrl, let monthlySentSms): + if boxed { + buffer.appendInt32(-594852657) + } + serializeString(termsUrl, buffer: buffer, boxed: false) + serializeInt32(monthlySentSms, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .eligibleToJoin(let termsUrl, let monthlySentSms): + return ("eligibleToJoin", [("termsUrl", termsUrl as Any), ("monthlySentSms", monthlySentSms as Any)]) + } + } + + public static func parse_eligibleToJoin(_ reader: BufferReader) -> EligibilityToJoin? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.smsjobs.EligibilityToJoin.eligibleToJoin(termsUrl: _1!, monthlySentSms: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.smsjobs { + enum Status: TypeConstructorDescription { + case status(flags: Int32, recentSent: Int32, recentSince: Int32, recentRemains: Int32, totalSent: Int32, totalSince: Int32, lastGiftSlug: String?, termsUrl: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .status(let flags, let recentSent, let recentSince, let recentRemains, let totalSent, let totalSince, let lastGiftSlug, let termsUrl): + if boxed { + buffer.appendInt32(720277905) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(recentSent, buffer: buffer, boxed: false) + serializeInt32(recentSince, buffer: buffer, boxed: false) + serializeInt32(recentRemains, buffer: buffer, boxed: false) + serializeInt32(totalSent, buffer: buffer, boxed: false) + serializeInt32(totalSince, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(lastGiftSlug!, buffer: buffer, boxed: false)} + serializeString(termsUrl, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .status(let flags, let recentSent, let recentSince, let recentRemains, let totalSent, let totalSince, let lastGiftSlug, let termsUrl): + return ("status", [("flags", flags as Any), ("recentSent", recentSent as Any), ("recentSince", recentSince as Any), ("recentRemains", recentRemains as Any), ("totalSent", totalSent as Any), ("totalSince", totalSince as Any), ("lastGiftSlug", lastGiftSlug as Any), ("termsUrl", termsUrl as Any)]) + } + } + + public static func parse_status(_ reader: BufferReader) -> Status? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: String? + if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) } + var _8: String? + _8 = parseString(reader) + 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 << 1) == 0) || _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.smsjobs.Status.status(flags: _1!, recentSent: _2!, recentSince: _3!, recentRemains: _4!, totalSent: _5!, totalSince: _6!, lastGiftSlug: _7, termsUrl: _8!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum BroadcastStats: TypeConstructorDescription { + case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, reactionsPerPost: Api.StatsAbsValueAndPrev, viewsPerStory: Api.StatsAbsValueAndPrev, sharesPerStory: Api.StatsAbsValueAndPrev, reactionsPerStory: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph, ivInteractionsGraph: Api.StatsGraph, viewsBySourceGraph: Api.StatsGraph, newFollowersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph, storyInteractionsGraph: Api.StatsGraph, storyReactionsByEmotionGraph: Api.StatsGraph, recentPostsInteractions: [Api.PostInteractionCounters]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let reactionsPerPost, let viewsPerStory, let sharesPerStory, let reactionsPerStory, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let reactionsByEmotionGraph, let storyInteractionsGraph, let storyReactionsByEmotionGraph, let recentPostsInteractions): + if boxed { + buffer.appendInt32(963421692) + } + period.serialize(buffer, true) + followers.serialize(buffer, true) + viewsPerPost.serialize(buffer, true) + sharesPerPost.serialize(buffer, true) + reactionsPerPost.serialize(buffer, true) + viewsPerStory.serialize(buffer, true) + sharesPerStory.serialize(buffer, true) + reactionsPerStory.serialize(buffer, true) + enabledNotifications.serialize(buffer, true) + growthGraph.serialize(buffer, true) + followersGraph.serialize(buffer, true) + muteGraph.serialize(buffer, true) + topHoursGraph.serialize(buffer, true) + interactionsGraph.serialize(buffer, true) + ivInteractionsGraph.serialize(buffer, true) + viewsBySourceGraph.serialize(buffer, true) + newFollowersBySourceGraph.serialize(buffer, true) + languagesGraph.serialize(buffer, true) + reactionsByEmotionGraph.serialize(buffer, true) + storyInteractionsGraph.serialize(buffer, true) + storyReactionsByEmotionGraph.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentPostsInteractions.count)) + for item in recentPostsInteractions { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let reactionsPerPost, let viewsPerStory, let sharesPerStory, let reactionsPerStory, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let ivInteractionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let reactionsByEmotionGraph, let storyInteractionsGraph, let storyReactionsByEmotionGraph, let recentPostsInteractions): + return ("broadcastStats", [("period", period as Any), ("followers", followers as Any), ("viewsPerPost", viewsPerPost as Any), ("sharesPerPost", sharesPerPost as Any), ("reactionsPerPost", reactionsPerPost as Any), ("viewsPerStory", viewsPerStory as Any), ("sharesPerStory", sharesPerStory as Any), ("reactionsPerStory", reactionsPerStory as Any), ("enabledNotifications", enabledNotifications as Any), ("growthGraph", growthGraph as Any), ("followersGraph", followersGraph as Any), ("muteGraph", muteGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("interactionsGraph", interactionsGraph as Any), ("ivInteractionsGraph", ivInteractionsGraph as Any), ("viewsBySourceGraph", viewsBySourceGraph as Any), ("newFollowersBySourceGraph", newFollowersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any), ("storyInteractionsGraph", storyInteractionsGraph as Any), ("storyReactionsByEmotionGraph", storyReactionsByEmotionGraph as Any), ("recentPostsInteractions", recentPostsInteractions as Any)]) + } + } + + public static func parse_broadcastStats(_ reader: BufferReader) -> BroadcastStats? { + var _1: Api.StatsDateRangeDays? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays + } + var _2: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _3: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _4: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _5: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _6: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _7: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _8: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _9: Api.StatsPercentValue? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue + } + var _10: Api.StatsGraph? + if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _11: Api.StatsGraph? + if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _12: Api.StatsGraph? + if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _13: Api.StatsGraph? + if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _14: Api.StatsGraph? + if let signature = reader.readInt32() { + _14 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _15: Api.StatsGraph? + if let signature = reader.readInt32() { + _15 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _16: Api.StatsGraph? + if let signature = reader.readInt32() { + _16 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _17: Api.StatsGraph? + if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _18: Api.StatsGraph? + if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _19: Api.StatsGraph? + if let signature = reader.readInt32() { + _19 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _20: Api.StatsGraph? + if let signature = reader.readInt32() { + _20 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _21: Api.StatsGraph? + if let signature = reader.readInt32() { + _21 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _22: [Api.PostInteractionCounters]? + if let _ = reader.readInt32() { + _22 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PostInteractionCounters.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 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + let _c16 = _16 != nil + let _c17 = _17 != nil + let _c18 = _18 != nil + let _c19 = _19 != nil + let _c20 = _20 != nil + let _c21 = _21 != nil + let _c22 = _22 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { + return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, reactionsPerPost: _5!, viewsPerStory: _6!, sharesPerStory: _7!, reactionsPerStory: _8!, enabledNotifications: _9!, growthGraph: _10!, followersGraph: _11!, muteGraph: _12!, topHoursGraph: _13!, interactionsGraph: _14!, ivInteractionsGraph: _15!, viewsBySourceGraph: _16!, newFollowersBySourceGraph: _17!, languagesGraph: _18!, reactionsByEmotionGraph: _19!, storyInteractionsGraph: _20!, storyReactionsByEmotionGraph: _21!, recentPostsInteractions: _22!) + } + else { + return nil + } + } + + } +} public extension Api.stats { enum MegagroupStats: TypeConstructorDescription { case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User]) @@ -1282,207 +1554,3 @@ public extension Api.updates { } } -public extension Api.updates { - enum Difference: TypeConstructorDescription { - case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) - case differenceEmpty(date: Int32, seq: Int32) - case differenceSlice(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], intermediateState: Api.updates.State) - case differenceTooLong(pts: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): - if boxed { - buffer.appendInt32(16030880) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newEncryptedMessages.count)) - for item in newEncryptedMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { - 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) - } - state.serialize(buffer, true) - break - case .differenceEmpty(let date, let seq): - if boxed { - buffer.appendInt32(1567990072) - } - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt32(seq, buffer: buffer, boxed: false) - break - case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): - if boxed { - buffer.appendInt32(-1459938943) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newEncryptedMessages.count)) - for item in newEncryptedMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { - 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) - } - intermediateState.serialize(buffer, true) - break - case .differenceTooLong(let pts): - if boxed { - buffer.appendInt32(1258196845) - } - serializeInt32(pts, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): - return ("difference", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) - case .differenceEmpty(let date, let seq): - return ("differenceEmpty", [("date", date as Any), ("seq", seq as Any)]) - case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): - return ("differenceSlice", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("intermediateState", intermediateState as Any)]) - case .differenceTooLong(let pts): - return ("differenceTooLong", [("pts", pts as Any)]) - } - } - - public static func parse_difference(_ reader: BufferReader) -> Difference? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.EncryptedMessage]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) - } - var _3: [Api.Update]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.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: Api.updates.State? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.updates.State - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) - } - else { - return nil - } - } - public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) - } - else { - return nil - } - } - public static func parse_differenceSlice(_ reader: BufferReader) -> Difference? { - var _1: [Api.Message]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _2: [Api.EncryptedMessage]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) - } - var _3: [Api.Update]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.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: Api.updates.State? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.updates.State - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) - } - else { - return nil - } - } - public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.updates.Difference.differenceTooLong(pts: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index 281a495dbf..0b6b16c01e 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -1,3 +1,207 @@ +public extension Api.updates { + enum Difference: TypeConstructorDescription { + case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) + case differenceEmpty(date: Int32, seq: Int32) + case differenceSlice(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], intermediateState: Api.updates.State) + case differenceTooLong(pts: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): + if boxed { + buffer.appendInt32(16030880) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + 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) + } + state.serialize(buffer, true) + break + case .differenceEmpty(let date, let seq): + if boxed { + buffer.appendInt32(1567990072) + } + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt32(seq, buffer: buffer, boxed: false) + break + case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): + if boxed { + buffer.appendInt32(-1459938943) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newEncryptedMessages.count)) + for item in newEncryptedMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + 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) + } + intermediateState.serialize(buffer, true) + break + case .differenceTooLong(let pts): + if boxed { + buffer.appendInt32(1258196845) + } + serializeInt32(pts, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .difference(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let state): + return ("difference", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("state", state as Any)]) + case .differenceEmpty(let date, let seq): + return ("differenceEmpty", [("date", date as Any), ("seq", seq as Any)]) + case .differenceSlice(let newMessages, let newEncryptedMessages, let otherUpdates, let chats, let users, let intermediateState): + return ("differenceSlice", [("newMessages", newMessages as Any), ("newEncryptedMessages", newEncryptedMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any), ("intermediateState", intermediateState as Any)]) + case .differenceTooLong(let pts): + return ("differenceTooLong", [("pts", pts as Any)]) + } + } + + public static func parse_difference(_ reader: BufferReader) -> Difference? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.EncryptedMessage]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) + } + var _3: [Api.Update]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.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: Api.updates.State? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.updates.State + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) + } + else { + return nil + } + } + public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) + } + else { + return nil + } + } + public static func parse_differenceSlice(_ reader: BufferReader) -> Difference? { + var _1: [Api.Message]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _2: [Api.EncryptedMessage]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.EncryptedMessage.self) + } + var _3: [Api.Update]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.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: Api.updates.State? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.updates.State + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) + } + else { + return nil + } + } + public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.updates.Difference.differenceTooLong(pts: _1!) + } + else { + return nil + } + } + + } +} public extension Api.updates { enum State: TypeConstructorDescription { case state(pts: Int32, qts: Int32, date: Int32, seq: Int32, unreadCount: Int32) @@ -248,6 +452,72 @@ public extension Api.upload { } } +public extension Api.users { + enum SavedMusic: TypeConstructorDescription { + case savedMusic(count: Int32, documents: [Api.Document]) + case savedMusicNotModified(count: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedMusic(let count, let documents): + if boxed { + buffer.appendInt32(883094167) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + item.serialize(buffer, true) + } + break + case .savedMusicNotModified(let count): + if boxed { + buffer.appendInt32(-477656412) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedMusic(let count, let documents): + return ("savedMusic", [("count", count as Any), ("documents", documents as Any)]) + case .savedMusicNotModified(let count): + return ("savedMusicNotModified", [("count", count as Any)]) + } + } + + public static func parse_savedMusic(_ reader: BufferReader) -> SavedMusic? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Document]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.users.SavedMusic.savedMusic(count: _1!, documents: _2!) + } + else { + return nil + } + } + public static func parse_savedMusicNotModified(_ reader: BufferReader) -> SavedMusic? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.users.SavedMusic.savedMusicNotModified(count: _1!) + } + else { + return nil + } + } + + } +} public extension Api.users { enum UserFull: TypeConstructorDescription { case userFull(fullUser: Api.UserFull, chats: [Api.Chat], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api39.swift b/submodules/TelegramApi/Sources/Api39.swift index 97ecfe0197..cc06769894 100644 --- a/submodules/TelegramApi/Sources/Api39.swift +++ b/submodules/TelegramApi/Sources/Api39.swift @@ -741,6 +741,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getSavedMusicIds(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-526557265) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getSavedMusicIds", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.SavedMusicIds? in + let reader = BufferReader(buffer) + var result: Api.account.SavedMusicIds? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.SavedMusicIds + } + return result + }) + } +} public extension Api.functions.account { static func getSavedRingtones(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -823,6 +838,23 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getUniqueGiftChatThemes(offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-25890913) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getUniqueGiftChatThemes", parameters: [("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.ChatThemes? in + let reader = BufferReader(buffer) + var result: Api.account.ChatThemes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.ChatThemes + } + return result + }) + } +} public extension Api.functions.account { static func getWallPaper(wallpaper: Api.InputWallPaper) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -1168,6 +1200,23 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func saveMusic(flags: Int32, id: Api.InputDocument, afterId: Api.InputDocument?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1301859671) + serializeInt32(flags, buffer: buffer, boxed: false) + id.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {afterId!.serialize(buffer, true)} + return (FunctionDescription(name: "account.saveMusic", parameters: [("flags", String(describing: flags)), ("id", String(describing: id)), ("afterId", String(describing: afterId))]), 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.account { static func saveRingtone(id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -1372,6 +1421,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func setMainProfileTab(tab: Api.ProfileTab) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1575909552) + tab.serialize(buffer, true) + return (FunctionDescription(name: "account.setMainProfileTab", parameters: [("tab", String(describing: tab))]), 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.account { static func setPrivacy(key: Api.InputPrivacyKey, rules: [Api.InputPrivacyRule]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -3596,6 +3660,22 @@ public extension Api.functions.channels { }) } } +public extension Api.functions.channels { + static func setMainProfileTab(channel: Api.InputChannel, tab: Api.ProfileTab) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(897842353) + channel.serialize(buffer, true) + tab.serialize(buffer, true) + return (FunctionDescription(name: "channels.setMainProfileTab", parameters: [("channel", String(describing: channel)), ("tab", String(describing: tab))]), 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.channels { static func setStickers(channel: Api.InputChannel, stickerset: Api.InputStickerSet) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -8586,12 +8666,12 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func setChatTheme(peer: Api.InputPeer, emoticon: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func setChatTheme(peer: Api.InputPeer, theme: Api.InputChatTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-432283329) + buffer.appendInt32(135398089) peer.serialize(buffer, true) - serializeString(emoticon, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.setChatTheme", parameters: [("peer", String(describing: peer)), ("emoticon", String(describing: emoticon))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + theme.serialize(buffer, true) + return (FunctionDescription(name: "messages.setChatTheme", parameters: [("peer", String(describing: peer)), ("theme", String(describing: theme))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -9284,6 +9364,21 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func checkCanSendGift(giftId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1060835895) + serializeInt64(giftId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.checkCanSendGift", parameters: [("giftId", String(describing: giftId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.CheckCanSendGiftResult? in + let reader = BufferReader(buffer) + var result: Api.payments.CheckCanSendGiftResult? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.CheckCanSendGiftResult + } + return result + }) + } +} public extension Api.functions.payments { static func checkGiftCode(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -9885,6 +9980,21 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func getUniqueStarGiftValueInfo(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1130737515) + serializeString(slug, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.getUniqueStarGiftValueInfo", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.UniqueStarGiftValueInfo? in + let reader = BufferReader(buffer) + var result: Api.payments.UniqueStarGiftValueInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.UniqueStarGiftValueInfo + } + return result + }) + } +} public extension Api.functions.payments { static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -12212,6 +12322,44 @@ public extension Api.functions.users { }) } } +public extension Api.functions.users { + static func getSavedMusic(id: Api.InputUser, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2022539235) + id.serialize(buffer, true) + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "users.getSavedMusic", parameters: [("id", String(describing: id)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.SavedMusic? in + let reader = BufferReader(buffer) + var result: Api.users.SavedMusic? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.users.SavedMusic + } + return result + }) + } +} +public extension Api.functions.users { + static func getSavedMusicByID(id: Api.InputUser, documents: [Api.InputDocument]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1970513129) + id.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "users.getSavedMusicByID", parameters: [("id", String(describing: id)), ("documents", String(describing: documents))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.users.SavedMusic? in + let reader = BufferReader(buffer) + var result: Api.users.SavedMusic? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.users.SavedMusic + } + return result + }) + } +} public extension Api.functions.users { static func getUsers(id: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.User]>) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 30ed7397d4..1ba4dc1b13 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -948,14 +948,14 @@ public extension Api { } public extension Api { enum ChatFull: TypeConstructorDescription { - case channelFull(flags: Int32, flags2: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, defaultSendAs: Api.Peer?, availableReactions: Api.ChatReactions?, reactionsLimit: Int32?, stories: Api.PeerStories?, wallpaper: Api.WallPaper?, boostsApplied: Int32?, boostsUnrestrict: Int32?, emojiset: Api.StickerSet?, botVerification: Api.BotVerification?, stargiftsCount: Int32?, sendPaidMessagesStars: Int64?) + case channelFull(flags: Int32, flags2: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, defaultSendAs: Api.Peer?, availableReactions: Api.ChatReactions?, reactionsLimit: Int32?, stories: Api.PeerStories?, wallpaper: Api.WallPaper?, boostsApplied: Int32?, boostsUnrestrict: Int32?, emojiset: Api.StickerSet?, botVerification: Api.BotVerification?, stargiftsCount: Int32?, sendPaidMessagesStars: Int64?, mainTab: Api.ProfileTab?) case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, availableReactions: Api.ChatReactions?, reactionsLimit: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channelFull(let flags, let flags2, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let defaultSendAs, let availableReactions, let reactionsLimit, let stories, let wallpaper, let boostsApplied, let boostsUnrestrict, let emojiset, let botVerification, let stargiftsCount, let sendPaidMessagesStars): + case .channelFull(let flags, let flags2, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let defaultSendAs, let availableReactions, let reactionsLimit, let stories, let wallpaper, let boostsApplied, let boostsUnrestrict, let emojiset, let botVerification, let stargiftsCount, let sendPaidMessagesStars, let mainTab): if boxed { - buffer.appendInt32(-529258018) + buffer.appendInt32(-455036259) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -1015,6 +1015,7 @@ public extension Api { if Int(flags2) & Int(1 << 17) != 0 {botVerification!.serialize(buffer, true)} if Int(flags2) & Int(1 << 18) != 0 {serializeInt32(stargiftsCount!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 21) != 0 {serializeInt64(sendPaidMessagesStars!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 22) != 0 {mainTab!.serialize(buffer, true)} break case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let availableReactions, let reactionsLimit): if boxed { @@ -1052,8 +1053,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channelFull(let flags, let flags2, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let defaultSendAs, let availableReactions, let reactionsLimit, let stories, let wallpaper, let boostsApplied, let boostsUnrestrict, let emojiset, let botVerification, let stargiftsCount, let sendPaidMessagesStars): - return ("channelFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("participantsCount", participantsCount as Any), ("adminsCount", adminsCount as Any), ("kickedCount", kickedCount as Any), ("bannedCount", bannedCount as Any), ("onlineCount", onlineCount as Any), ("readInboxMaxId", readInboxMaxId as Any), ("readOutboxMaxId", readOutboxMaxId as Any), ("unreadCount", unreadCount as Any), ("chatPhoto", chatPhoto as Any), ("notifySettings", notifySettings as Any), ("exportedInvite", exportedInvite as Any), ("botInfo", botInfo as Any), ("migratedFromChatId", migratedFromChatId as Any), ("migratedFromMaxId", migratedFromMaxId as Any), ("pinnedMsgId", pinnedMsgId as Any), ("stickerset", stickerset as Any), ("availableMinId", availableMinId as Any), ("folderId", folderId as Any), ("linkedChatId", linkedChatId as Any), ("location", location as Any), ("slowmodeSeconds", slowmodeSeconds as Any), ("slowmodeNextSendDate", slowmodeNextSendDate as Any), ("statsDc", statsDc as Any), ("pts", pts as Any), ("call", call as Any), ("ttlPeriod", ttlPeriod as Any), ("pendingSuggestions", pendingSuggestions as Any), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs as Any), ("themeEmoticon", themeEmoticon as Any), ("requestsPending", requestsPending as Any), ("recentRequesters", recentRequesters as Any), ("defaultSendAs", defaultSendAs as Any), ("availableReactions", availableReactions as Any), ("reactionsLimit", reactionsLimit as Any), ("stories", stories as Any), ("wallpaper", wallpaper as Any), ("boostsApplied", boostsApplied as Any), ("boostsUnrestrict", boostsUnrestrict as Any), ("emojiset", emojiset as Any), ("botVerification", botVerification as Any), ("stargiftsCount", stargiftsCount as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any)]) + case .channelFull(let flags, let flags2, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let defaultSendAs, let availableReactions, let reactionsLimit, let stories, let wallpaper, let boostsApplied, let boostsUnrestrict, let emojiset, let botVerification, let stargiftsCount, let sendPaidMessagesStars, let mainTab): + return ("channelFull", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("about", about as Any), ("participantsCount", participantsCount as Any), ("adminsCount", adminsCount as Any), ("kickedCount", kickedCount as Any), ("bannedCount", bannedCount as Any), ("onlineCount", onlineCount as Any), ("readInboxMaxId", readInboxMaxId as Any), ("readOutboxMaxId", readOutboxMaxId as Any), ("unreadCount", unreadCount as Any), ("chatPhoto", chatPhoto as Any), ("notifySettings", notifySettings as Any), ("exportedInvite", exportedInvite as Any), ("botInfo", botInfo as Any), ("migratedFromChatId", migratedFromChatId as Any), ("migratedFromMaxId", migratedFromMaxId as Any), ("pinnedMsgId", pinnedMsgId as Any), ("stickerset", stickerset as Any), ("availableMinId", availableMinId as Any), ("folderId", folderId as Any), ("linkedChatId", linkedChatId as Any), ("location", location as Any), ("slowmodeSeconds", slowmodeSeconds as Any), ("slowmodeNextSendDate", slowmodeNextSendDate as Any), ("statsDc", statsDc as Any), ("pts", pts as Any), ("call", call as Any), ("ttlPeriod", ttlPeriod as Any), ("pendingSuggestions", pendingSuggestions as Any), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs as Any), ("themeEmoticon", themeEmoticon as Any), ("requestsPending", requestsPending as Any), ("recentRequesters", recentRequesters as Any), ("defaultSendAs", defaultSendAs as Any), ("availableReactions", availableReactions as Any), ("reactionsLimit", reactionsLimit as Any), ("stories", stories as Any), ("wallpaper", wallpaper as Any), ("boostsApplied", boostsApplied as Any), ("boostsUnrestrict", boostsUnrestrict as Any), ("emojiset", emojiset as Any), ("botVerification", botVerification as Any), ("stargiftsCount", stargiftsCount as Any), ("sendPaidMessagesStars", sendPaidMessagesStars as Any), ("mainTab", mainTab as Any)]) case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let availableReactions, let reactionsLimit): return ("chatFull", [("flags", flags as Any), ("id", id as Any), ("about", about as Any), ("participants", participants as Any), ("chatPhoto", chatPhoto as Any), ("notifySettings", notifySettings as Any), ("exportedInvite", exportedInvite as Any), ("botInfo", botInfo as Any), ("pinnedMsgId", pinnedMsgId as Any), ("folderId", folderId as Any), ("call", call as Any), ("ttlPeriod", ttlPeriod as Any), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs as Any), ("themeEmoticon", themeEmoticon as Any), ("requestsPending", requestsPending as Any), ("recentRequesters", recentRequesters as Any), ("availableReactions", availableReactions as Any), ("reactionsLimit", reactionsLimit as Any)]) } @@ -1184,6 +1185,10 @@ public extension Api { if Int(_2!) & Int(1 << 18) != 0 {_45 = reader.readInt32() } var _46: Int64? if Int(_2!) & Int(1 << 21) != 0 {_46 = reader.readInt64() } + var _47: Api.ProfileTab? + if Int(_2!) & Int(1 << 22) != 0 {if let signature = reader.readInt32() { + _47 = Api.parse(reader, signature: signature) as? Api.ProfileTab + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -1230,8 +1235,9 @@ public extension Api { let _c44 = (Int(_2!) & Int(1 << 17) == 0) || _44 != nil let _c45 = (Int(_2!) & Int(1 << 18) == 0) || _45 != nil let _c46 = (Int(_2!) & Int(1 << 21) == 0) || _46 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 && _c44 && _c45 && _c46 { - return Api.ChatFull.channelFull(flags: _1!, flags2: _2!, id: _3!, about: _4!, participantsCount: _5, adminsCount: _6, kickedCount: _7, bannedCount: _8, onlineCount: _9, readInboxMaxId: _10!, readOutboxMaxId: _11!, unreadCount: _12!, chatPhoto: _13!, notifySettings: _14!, exportedInvite: _15, botInfo: _16!, migratedFromChatId: _17, migratedFromMaxId: _18, pinnedMsgId: _19, stickerset: _20, availableMinId: _21, folderId: _22, linkedChatId: _23, location: _24, slowmodeSeconds: _25, slowmodeNextSendDate: _26, statsDc: _27, pts: _28!, call: _29, ttlPeriod: _30, pendingSuggestions: _31, groupcallDefaultJoinAs: _32, themeEmoticon: _33, requestsPending: _34, recentRequesters: _35, defaultSendAs: _36, availableReactions: _37, reactionsLimit: _38, stories: _39, wallpaper: _40, boostsApplied: _41, boostsUnrestrict: _42, emojiset: _43, botVerification: _44, stargiftsCount: _45, sendPaidMessagesStars: _46) + let _c47 = (Int(_2!) & Int(1 << 22) == 0) || _47 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 && _c44 && _c45 && _c46 && _c47 { + return Api.ChatFull.channelFull(flags: _1!, flags2: _2!, id: _3!, about: _4!, participantsCount: _5, adminsCount: _6, kickedCount: _7, bannedCount: _8, onlineCount: _9, readInboxMaxId: _10!, readOutboxMaxId: _11!, unreadCount: _12!, chatPhoto: _13!, notifySettings: _14!, exportedInvite: _15, botInfo: _16!, migratedFromChatId: _17, migratedFromMaxId: _18, pinnedMsgId: _19, stickerset: _20, availableMinId: _21, folderId: _22, linkedChatId: _23, location: _24, slowmodeSeconds: _25, slowmodeNextSendDate: _26, statsDc: _27, pts: _28!, call: _29, ttlPeriod: _30, pendingSuggestions: _31, groupcallDefaultJoinAs: _32, themeEmoticon: _33, requestsPending: _34, recentRequesters: _35, defaultSendAs: _36, availableReactions: _37, reactionsLimit: _38, stories: _39, wallpaper: _40, boostsApplied: _41, boostsUnrestrict: _42, emojiset: _43, botVerification: _44, stargiftsCount: _45, sendPaidMessagesStars: _46, mainTab: _47) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api5.swift b/submodules/TelegramApi/Sources/Api5.swift index 88cff55c26..2ce916e3f5 100644 --- a/submodules/TelegramApi/Sources/Api5.swift +++ b/submodules/TelegramApi/Sources/Api5.swift @@ -392,6 +392,74 @@ public extension Api { } } +public extension Api { + enum ChatTheme: TypeConstructorDescription { + case chatTheme(emoticon: String) + case chatThemeUniqueGift(gift: Api.StarGift, themeSettings: [Api.ThemeSettings]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatTheme(let emoticon): + if boxed { + buffer.appendInt32(-1008731132) + } + serializeString(emoticon, buffer: buffer, boxed: false) + break + case .chatThemeUniqueGift(let gift, let themeSettings): + if boxed { + buffer.appendInt32(878246344) + } + gift.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(themeSettings.count)) + for item in themeSettings { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatTheme(let emoticon): + return ("chatTheme", [("emoticon", emoticon as Any)]) + case .chatThemeUniqueGift(let gift, let themeSettings): + return ("chatThemeUniqueGift", [("gift", gift as Any), ("themeSettings", themeSettings as Any)]) + } + } + + public static func parse_chatTheme(_ reader: BufferReader) -> ChatTheme? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.ChatTheme.chatTheme(emoticon: _1!) + } + else { + return nil + } + } + public static func parse_chatThemeUniqueGift(_ reader: BufferReader) -> ChatTheme? { + var _1: Api.StarGift? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StarGift + } + var _2: [Api.ThemeSettings]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ThemeSettings.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.ChatTheme.chatThemeUniqueGift(gift: _1!, themeSettings: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum CodeSettings: TypeConstructorDescription { case codeSettings(flags: Int32, logoutTokens: [Buffer]?, token: String?, appSandbox: Api.Bool?) @@ -1424,153 +1492,3 @@ public extension Api { } } -public extension Api { - enum DisallowedGiftsSettings: TypeConstructorDescription { - case disallowedGiftsSettings(flags: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .disallowedGiftsSettings(let flags): - if boxed { - buffer.appendInt32(1911715524) - } - serializeInt32(flags, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .disallowedGiftsSettings(let flags): - return ("disallowedGiftsSettings", [("flags", flags as Any)]) - } - } - - public static func parse_disallowedGiftsSettings(_ reader: BufferReader) -> DisallowedGiftsSettings? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: _1!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum Document: TypeConstructorDescription { - case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int64, thumbs: [Api.PhotoSize]?, videoThumbs: [Api.VideoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute]) - case documentEmpty(id: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes): - if boxed { - buffer.appendInt32(-1881881384) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeBytes(fileReference, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - serializeInt64(size, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(thumbs!.count)) - for item in thumbs! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(videoThumbs!.count)) - for item in videoThumbs! { - item.serialize(buffer, true) - }} - serializeInt32(dcId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes.count)) - for item in attributes { - item.serialize(buffer, true) - } - break - case .documentEmpty(let id): - if boxed { - buffer.appendInt32(922273905) - } - serializeInt64(id, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes): - return ("document", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("date", date as Any), ("mimeType", mimeType as Any), ("size", size as Any), ("thumbs", thumbs as Any), ("videoThumbs", videoThumbs as Any), ("dcId", dcId as Any), ("attributes", attributes as Any)]) - case .documentEmpty(let id): - return ("documentEmpty", [("id", id as Any)]) - } - } - - public static func parse_document(_ reader: BufferReader) -> Document? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Buffer? - _4 = parseBytes(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: String? - _6 = parseString(reader) - var _7: Int64? - _7 = reader.readInt64() - var _8: [Api.PhotoSize]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) - } } - var _9: [Api.VideoSize]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.VideoSize.self) - } } - var _10: Int32? - _10 = reader.readInt32() - var _11: [Api.DocumentAttribute]? - if let _ = reader.readInt32() { - _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.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 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, videoThumbs: _9, dcId: _10!, attributes: _11!) - } - else { - return nil - } - } - public static func parse_documentEmpty(_ reader: BufferReader) -> Document? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Document.documentEmpty(id: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api6.swift b/submodules/TelegramApi/Sources/Api6.swift index c149e4cc89..ca7545a09a 100644 --- a/submodules/TelegramApi/Sources/Api6.swift +++ b/submodules/TelegramApi/Sources/Api6.swift @@ -1,3 +1,153 @@ +public extension Api { + enum DisallowedGiftsSettings: TypeConstructorDescription { + case disallowedGiftsSettings(flags: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .disallowedGiftsSettings(let flags): + if boxed { + buffer.appendInt32(1911715524) + } + serializeInt32(flags, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .disallowedGiftsSettings(let flags): + return ("disallowedGiftsSettings", [("flags", flags as Any)]) + } + } + + public static func parse_disallowedGiftsSettings(_ reader: BufferReader) -> DisallowedGiftsSettings? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum Document: TypeConstructorDescription { + case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int64, thumbs: [Api.PhotoSize]?, videoThumbs: [Api.VideoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute]) + case documentEmpty(id: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes): + if boxed { + buffer.appendInt32(-1881881384) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeBytes(fileReference, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt64(size, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(thumbs!.count)) + for item in thumbs! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(videoThumbs!.count)) + for item in videoThumbs! { + item.serialize(buffer, true) + }} + serializeInt32(dcId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) + } + break + case .documentEmpty(let id): + if boxed { + buffer.appendInt32(922273905) + } + serializeInt64(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let videoThumbs, let dcId, let attributes): + return ("document", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("date", date as Any), ("mimeType", mimeType as Any), ("size", size as Any), ("thumbs", thumbs as Any), ("videoThumbs", videoThumbs as Any), ("dcId", dcId as Any), ("attributes", attributes as Any)]) + case .documentEmpty(let id): + return ("documentEmpty", [("id", id as Any)]) + } + } + + public static func parse_document(_ reader: BufferReader) -> Document? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Buffer? + _4 = parseBytes(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: String? + _6 = parseString(reader) + var _7: Int64? + _7 = reader.readInt64() + var _8: [Api.PhotoSize]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) + } } + var _9: [Api.VideoSize]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.VideoSize.self) + } } + var _10: Int32? + _10 = reader.readInt32() + var _11: [Api.DocumentAttribute]? + if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.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 = _7 != nil + let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { + return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, videoThumbs: _9, dcId: _10!, attributes: _11!) + } + else { + return nil + } + } + public static func parse_documentEmpty(_ reader: BufferReader) -> Document? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Document.documentEmpty(id: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum DocumentAttribute: TypeConstructorDescription { case documentAttributeAnimated @@ -944,227 +1094,3 @@ public extension Api { } } -public extension Api { - enum EmojiURL: TypeConstructorDescription { - case emojiURL(url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .emojiURL(let url): - if boxed { - buffer.appendInt32(-1519029347) - } - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .emojiURL(let url): - return ("emojiURL", [("url", url as Any)]) - } - } - - public static func parse_emojiURL(_ reader: BufferReader) -> EmojiURL? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.EmojiURL.emojiURL(url: _1!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum EncryptedChat: TypeConstructorDescription { - case encryptedChat(id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64) - case encryptedChatDiscarded(flags: Int32, id: Int32) - case encryptedChatEmpty(id: Int32) - case encryptedChatRequested(flags: Int32, folderId: Int32?, id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gA: Buffer) - case encryptedChatWaiting(id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .encryptedChat(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint): - if boxed { - buffer.appendInt32(1643173063) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(adminId, buffer: buffer, boxed: false) - serializeInt64(participantId, buffer: buffer, boxed: false) - serializeBytes(gAOrB, buffer: buffer, boxed: false) - serializeInt64(keyFingerprint, buffer: buffer, boxed: false) - break - case .encryptedChatDiscarded(let flags, let id): - if boxed { - buffer.appendInt32(505183301) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - break - case .encryptedChatEmpty(let id): - if boxed { - buffer.appendInt32(-1417756512) - } - serializeInt32(id, buffer: buffer, boxed: false) - break - case .encryptedChatRequested(let flags, let folderId, let id, let accessHash, let date, let adminId, let participantId, let gA): - if boxed { - buffer.appendInt32(1223809356) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(adminId, buffer: buffer, boxed: false) - serializeInt64(participantId, buffer: buffer, boxed: false) - serializeBytes(gA, buffer: buffer, boxed: false) - break - case .encryptedChatWaiting(let id, let accessHash, let date, let adminId, let participantId): - if boxed { - buffer.appendInt32(1722964307) - } - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(adminId, buffer: buffer, boxed: false) - serializeInt64(participantId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .encryptedChat(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint): - return ("encryptedChat", [("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any)]) - case .encryptedChatDiscarded(let flags, let id): - return ("encryptedChatDiscarded", [("flags", flags as Any), ("id", id as Any)]) - case .encryptedChatEmpty(let id): - return ("encryptedChatEmpty", [("id", id as Any)]) - case .encryptedChatRequested(let flags, let folderId, let id, let accessHash, let date, let adminId, let participantId, let gA): - return ("encryptedChatRequested", [("flags", flags as Any), ("folderId", folderId as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gA", gA as Any)]) - case .encryptedChatWaiting(let id, let accessHash, let date, let adminId, let participantId): - return ("encryptedChatWaiting", [("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any)]) - } - } - - public static func parse_encryptedChat(_ reader: BufferReader) -> EncryptedChat? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int64? - _4 = reader.readInt64() - var _5: Int64? - _5 = reader.readInt64() - var _6: Buffer? - _6 = parseBytes(reader) - var _7: Int64? - _7 = reader.readInt64() - 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.EncryptedChat.encryptedChat(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!, gAOrB: _6!, keyFingerprint: _7!) - } - else { - return nil - } - } - public static func parse_encryptedChatDiscarded(_ reader: BufferReader) -> EncryptedChat? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EncryptedChat.encryptedChatDiscarded(flags: _1!, id: _2!) - } - else { - return nil - } - } - public static func parse_encryptedChatEmpty(_ reader: BufferReader) -> EncryptedChat? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.EncryptedChat.encryptedChatEmpty(id: _1!) - } - else { - return nil - } - } - public static func parse_encryptedChatRequested(_ reader: BufferReader) -> EncryptedChat? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int64? - _4 = reader.readInt64() - var _5: Int32? - _5 = reader.readInt32() - var _6: Int64? - _6 = reader.readInt64() - var _7: Int64? - _7 = reader.readInt64() - var _8: Buffer? - _8 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.EncryptedChat.encryptedChatRequested(flags: _1!, folderId: _2, id: _3!, accessHash: _4!, date: _5!, adminId: _6!, participantId: _7!, gA: _8!) - } - else { - return nil - } - } - public static func parse_encryptedChatWaiting(_ reader: BufferReader) -> EncryptedChat? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int64? - _4 = reader.readInt64() - var _5: Int64? - _5 = reader.readInt64() - 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.EncryptedChat.encryptedChatWaiting(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index 4529fac0e5..97dd79ac02 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -1,3 +1,227 @@ +public extension Api { + enum EmojiURL: TypeConstructorDescription { + case emojiURL(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .emojiURL(let url): + if boxed { + buffer.appendInt32(-1519029347) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .emojiURL(let url): + return ("emojiURL", [("url", url as Any)]) + } + } + + public static func parse_emojiURL(_ reader: BufferReader) -> EmojiURL? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.EmojiURL.emojiURL(url: _1!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum EncryptedChat: TypeConstructorDescription { + case encryptedChat(id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64) + case encryptedChatDiscarded(flags: Int32, id: Int32) + case encryptedChatEmpty(id: Int32) + case encryptedChatRequested(flags: Int32, folderId: Int32?, id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gA: Buffer) + case encryptedChatWaiting(id: Int32, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .encryptedChat(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint): + if boxed { + buffer.appendInt32(1643173063) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(adminId, buffer: buffer, boxed: false) + serializeInt64(participantId, buffer: buffer, boxed: false) + serializeBytes(gAOrB, buffer: buffer, boxed: false) + serializeInt64(keyFingerprint, buffer: buffer, boxed: false) + break + case .encryptedChatDiscarded(let flags, let id): + if boxed { + buffer.appendInt32(505183301) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + break + case .encryptedChatEmpty(let id): + if boxed { + buffer.appendInt32(-1417756512) + } + serializeInt32(id, buffer: buffer, boxed: false) + break + case .encryptedChatRequested(let flags, let folderId, let id, let accessHash, let date, let adminId, let participantId, let gA): + if boxed { + buffer.appendInt32(1223809356) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(adminId, buffer: buffer, boxed: false) + serializeInt64(participantId, buffer: buffer, boxed: false) + serializeBytes(gA, buffer: buffer, boxed: false) + break + case .encryptedChatWaiting(let id, let accessHash, let date, let adminId, let participantId): + if boxed { + buffer.appendInt32(1722964307) + } + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(adminId, buffer: buffer, boxed: false) + serializeInt64(participantId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .encryptedChat(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint): + return ("encryptedChat", [("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any)]) + case .encryptedChatDiscarded(let flags, let id): + return ("encryptedChatDiscarded", [("flags", flags as Any), ("id", id as Any)]) + case .encryptedChatEmpty(let id): + return ("encryptedChatEmpty", [("id", id as Any)]) + case .encryptedChatRequested(let flags, let folderId, let id, let accessHash, let date, let adminId, let participantId, let gA): + return ("encryptedChatRequested", [("flags", flags as Any), ("folderId", folderId as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gA", gA as Any)]) + case .encryptedChatWaiting(let id, let accessHash, let date, let adminId, let participantId): + return ("encryptedChatWaiting", [("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any)]) + } + } + + public static func parse_encryptedChat(_ reader: BufferReader) -> EncryptedChat? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int64? + _4 = reader.readInt64() + var _5: Int64? + _5 = reader.readInt64() + var _6: Buffer? + _6 = parseBytes(reader) + var _7: Int64? + _7 = reader.readInt64() + 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.EncryptedChat.encryptedChat(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!, gAOrB: _6!, keyFingerprint: _7!) + } + else { + return nil + } + } + public static func parse_encryptedChatDiscarded(_ reader: BufferReader) -> EncryptedChat? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.EncryptedChat.encryptedChatDiscarded(flags: _1!, id: _2!) + } + else { + return nil + } + } + public static func parse_encryptedChatEmpty(_ reader: BufferReader) -> EncryptedChat? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.EncryptedChat.encryptedChatEmpty(id: _1!) + } + else { + return nil + } + } + public static func parse_encryptedChatRequested(_ reader: BufferReader) -> EncryptedChat? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int64? + _4 = reader.readInt64() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int64? + _6 = reader.readInt64() + var _7: Int64? + _7 = reader.readInt64() + var _8: Buffer? + _8 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.EncryptedChat.encryptedChatRequested(flags: _1!, folderId: _2, id: _3!, accessHash: _4!, date: _5!, adminId: _6!, participantId: _7!, gA: _8!) + } + else { + return nil + } + } + public static func parse_encryptedChatWaiting(_ reader: BufferReader) -> EncryptedChat? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int64? + _4 = reader.readInt64() + var _5: Int64? + _5 = reader.readInt64() + 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.EncryptedChat.encryptedChatWaiting(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!) + } + else { + return nil + } + } + + } +} public extension Api { enum EncryptedFile: TypeConstructorDescription { case encryptedFile(id: Int64, accessHash: Int64, size: Int64, dcId: Int32, keyFingerprint: Int32) @@ -1230,257 +1454,3 @@ public extension Api { } } -public extension Api { - enum GroupCallParticipantVideoSourceGroup: TypeConstructorDescription { - case groupCallParticipantVideoSourceGroup(semantics: String, sources: [Int32]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallParticipantVideoSourceGroup(let semantics, let sources): - if boxed { - buffer.appendInt32(-592373577) - } - serializeString(semantics, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sources.count)) - for item in sources { - serializeInt32(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallParticipantVideoSourceGroup(let semantics, let sources): - return ("groupCallParticipantVideoSourceGroup", [("semantics", semantics as Any), ("sources", sources as Any)]) - } - } - - public static func parse_groupCallParticipantVideoSourceGroup(_ reader: BufferReader) -> GroupCallParticipantVideoSourceGroup? { - var _1: String? - _1 = parseString(reader) - var _2: [Int32]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.GroupCallParticipantVideoSourceGroup.groupCallParticipantVideoSourceGroup(semantics: _1!, sources: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum GroupCallStreamChannel: TypeConstructorDescription { - case groupCallStreamChannel(channel: Int32, scale: Int32, lastTimestampMs: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallStreamChannel(let channel, let scale, let lastTimestampMs): - if boxed { - buffer.appendInt32(-2132064081) - } - serializeInt32(channel, buffer: buffer, boxed: false) - serializeInt32(scale, buffer: buffer, boxed: false) - serializeInt64(lastTimestampMs, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallStreamChannel(let channel, let scale, let lastTimestampMs): - return ("groupCallStreamChannel", [("channel", channel as Any), ("scale", scale as Any), ("lastTimestampMs", lastTimestampMs as Any)]) - } - } - - public static func parse_groupCallStreamChannel(_ reader: BufferReader) -> GroupCallStreamChannel? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.GroupCallStreamChannel.groupCallStreamChannel(channel: _1!, scale: _2!, lastTimestampMs: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum HighScore: TypeConstructorDescription { - case highScore(pos: Int32, userId: Int64, score: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .highScore(let pos, let userId, let score): - if boxed { - buffer.appendInt32(1940093419) - } - serializeInt32(pos, buffer: buffer, boxed: false) - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt32(score, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .highScore(let pos, let userId, let score): - return ("highScore", [("pos", pos as Any), ("userId", userId as Any), ("score", score as Any)]) - } - } - - public static func parse_highScore(_ reader: BufferReader) -> HighScore? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum ImportedContact: TypeConstructorDescription { - case importedContact(userId: Int64, clientId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .importedContact(let userId, let clientId): - if boxed { - buffer.appendInt32(-1052885936) - } - serializeInt64(userId, buffer: buffer, boxed: false) - serializeInt64(clientId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .importedContact(let userId, let clientId): - return ("importedContact", [("userId", userId as Any), ("clientId", clientId as Any)]) - } - } - - public static func parse_importedContact(_ reader: BufferReader) -> ImportedContact? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum InlineBotSwitchPM: TypeConstructorDescription { - case inlineBotSwitchPM(text: String, startParam: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inlineBotSwitchPM(let text, let startParam): - if boxed { - buffer.appendInt32(1008755359) - } - serializeString(text, buffer: buffer, boxed: false) - serializeString(startParam, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inlineBotSwitchPM(let text, let startParam): - return ("inlineBotSwitchPM", [("text", text as Any), ("startParam", startParam as Any)]) - } - } - - public static func parse_inlineBotSwitchPM(_ reader: BufferReader) -> InlineBotSwitchPM? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InlineBotSwitchPM.inlineBotSwitchPM(text: _1!, startParam: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum InlineBotWebView: TypeConstructorDescription { - case inlineBotWebView(text: String, url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inlineBotWebView(let text, let url): - if boxed { - buffer.appendInt32(-1250781739) - } - serializeString(text, buffer: buffer, boxed: false) - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inlineBotWebView(let text, let url): - return ("inlineBotWebView", [("text", text as Any), ("url", url as Any)]) - } - } - - public static func parse_inlineBotWebView(_ reader: BufferReader) -> InlineBotWebView? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InlineBotWebView.inlineBotWebView(text: _1!, url: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api8.swift b/submodules/TelegramApi/Sources/Api8.swift index c40175faf0..7d4678a8c1 100644 --- a/submodules/TelegramApi/Sources/Api8.swift +++ b/submodules/TelegramApi/Sources/Api8.swift @@ -1,3 +1,257 @@ +public extension Api { + enum GroupCallParticipantVideoSourceGroup: TypeConstructorDescription { + case groupCallParticipantVideoSourceGroup(semantics: String, sources: [Int32]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCallParticipantVideoSourceGroup(let semantics, let sources): + if boxed { + buffer.appendInt32(-592373577) + } + serializeString(semantics, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sources.count)) + for item in sources { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCallParticipantVideoSourceGroup(let semantics, let sources): + return ("groupCallParticipantVideoSourceGroup", [("semantics", semantics as Any), ("sources", sources as Any)]) + } + } + + public static func parse_groupCallParticipantVideoSourceGroup(_ reader: BufferReader) -> GroupCallParticipantVideoSourceGroup? { + var _1: String? + _1 = parseString(reader) + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.GroupCallParticipantVideoSourceGroup.groupCallParticipantVideoSourceGroup(semantics: _1!, sources: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum GroupCallStreamChannel: TypeConstructorDescription { + case groupCallStreamChannel(channel: Int32, scale: Int32, lastTimestampMs: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCallStreamChannel(let channel, let scale, let lastTimestampMs): + if boxed { + buffer.appendInt32(-2132064081) + } + serializeInt32(channel, buffer: buffer, boxed: false) + serializeInt32(scale, buffer: buffer, boxed: false) + serializeInt64(lastTimestampMs, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCallStreamChannel(let channel, let scale, let lastTimestampMs): + return ("groupCallStreamChannel", [("channel", channel as Any), ("scale", scale as Any), ("lastTimestampMs", lastTimestampMs as Any)]) + } + } + + public static func parse_groupCallStreamChannel(_ reader: BufferReader) -> GroupCallStreamChannel? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.GroupCallStreamChannel.groupCallStreamChannel(channel: _1!, scale: _2!, lastTimestampMs: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum HighScore: TypeConstructorDescription { + case highScore(pos: Int32, userId: Int64, score: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .highScore(let pos, let userId, let score): + if boxed { + buffer.appendInt32(1940093419) + } + serializeInt32(pos, buffer: buffer, boxed: false) + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt32(score, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .highScore(let pos, let userId, let score): + return ("highScore", [("pos", pos as Any), ("userId", userId as Any), ("score", score as Any)]) + } + } + + public static func parse_highScore(_ reader: BufferReader) -> HighScore? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum ImportedContact: TypeConstructorDescription { + case importedContact(userId: Int64, clientId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .importedContact(let userId, let clientId): + if boxed { + buffer.appendInt32(-1052885936) + } + serializeInt64(userId, buffer: buffer, boxed: false) + serializeInt64(clientId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .importedContact(let userId, let clientId): + return ("importedContact", [("userId", userId as Any), ("clientId", clientId as Any)]) + } + } + + public static func parse_importedContact(_ reader: BufferReader) -> ImportedContact? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum InlineBotSwitchPM: TypeConstructorDescription { + case inlineBotSwitchPM(text: String, startParam: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inlineBotSwitchPM(let text, let startParam): + if boxed { + buffer.appendInt32(1008755359) + } + serializeString(text, buffer: buffer, boxed: false) + serializeString(startParam, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inlineBotSwitchPM(let text, let startParam): + return ("inlineBotSwitchPM", [("text", text as Any), ("startParam", startParam as Any)]) + } + } + + public static func parse_inlineBotSwitchPM(_ reader: BufferReader) -> InlineBotSwitchPM? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InlineBotSwitchPM.inlineBotSwitchPM(text: _1!, startParam: _2!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum InlineBotWebView: TypeConstructorDescription { + case inlineBotWebView(text: String, url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inlineBotWebView(let text, let url): + if boxed { + buffer.appendInt32(-1250781739) + } + serializeString(text, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inlineBotWebView(let text, let url): + return ("inlineBotWebView", [("text", text as Any), ("url", url as Any)]) + } + } + + public static func parse_inlineBotWebView(_ reader: BufferReader) -> InlineBotWebView? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InlineBotWebView.inlineBotWebView(text: _1!, url: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum InlineQueryPeerType: TypeConstructorDescription { case inlineQueryPeerTypeBotPM @@ -992,227 +1246,3 @@ public extension Api { } } -public extension Api { - enum InputBusinessGreetingMessage: TypeConstructorDescription { - case inputBusinessGreetingMessage(shortcutId: Int32, recipients: Api.InputBusinessRecipients, noActivityDays: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): - if boxed { - buffer.appendInt32(26528571) - } - serializeInt32(shortcutId, buffer: buffer, boxed: false) - recipients.serialize(buffer, true) - serializeInt32(noActivityDays, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): - return ("inputBusinessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) - } - } - - public static func parse_inputBusinessGreetingMessage(_ reader: BufferReader) -> InputBusinessGreetingMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.InputBusinessRecipients? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.InputBusinessRecipients - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum InputBusinessIntro: TypeConstructorDescription { - case inputBusinessIntro(flags: Int32, title: String, description: String, sticker: Api.InputDocument?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputBusinessIntro(let flags, let title, let description, let sticker): - if boxed { - buffer.appendInt32(163867085) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputBusinessIntro(let flags, let title, let description, let sticker): - return ("inputBusinessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)]) - } - } - - public static func parse_inputBusinessIntro(_ reader: BufferReader) -> InputBusinessIntro? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: String? - _3 = parseString(reader) - var _4: Api.InputDocument? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.InputDocument - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessIntro.inputBusinessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) - } - else { - return nil - } - } - - } -} -public extension Api { - enum InputBusinessRecipients: TypeConstructorDescription { - case inputBusinessRecipients(flags: Int32, users: [Api.InputUser]?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputBusinessRecipients(let flags, let users): - if boxed { - buffer.appendInt32(1871393450) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 4) != 0 {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 .inputBusinessRecipients(let flags, let users): - return ("inputBusinessRecipients", [("flags", flags as Any), ("users", users as Any)]) - } - } - - public static func parse_inputBusinessRecipients(_ reader: BufferReader) -> InputBusinessRecipients? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.InputUser]? - if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) - } } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil - if _c1 && _c2 { - return Api.InputBusinessRecipients.inputBusinessRecipients(flags: _1!, users: _2) - } - else { - return nil - } - } - - } -} -public extension Api { - indirect enum InputChannel: TypeConstructorDescription { - case inputChannel(channelId: Int64, accessHash: Int64) - case inputChannelEmpty - case inputChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int64) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputChannel(let channelId, let accessHash): - if boxed { - buffer.appendInt32(-212145112) - } - serializeInt64(channelId, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - break - case .inputChannelEmpty: - if boxed { - buffer.appendInt32(-292807034) - } - - break - case .inputChannelFromMessage(let peer, let msgId, let channelId): - if boxed { - buffer.appendInt32(1536380829) - } - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt64(channelId, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputChannel(let channelId, let accessHash): - return ("inputChannel", [("channelId", channelId as Any), ("accessHash", accessHash as Any)]) - case .inputChannelEmpty: - return ("inputChannelEmpty", []) - case .inputChannelFromMessage(let peer, let msgId, let channelId): - return ("inputChannelFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("channelId", channelId as Any)]) - } - } - - public static func parse_inputChannel(_ reader: BufferReader) -> InputChannel? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputChannel.inputChannel(channelId: _1!, accessHash: _2!) - } - else { - return nil - } - } - public static func parse_inputChannelEmpty(_ reader: BufferReader) -> InputChannel? { - return Api.InputChannel.inputChannelEmpty - } - public static func parse_inputChannelFromMessage(_ reader: BufferReader) -> InputChannel? { - var _1: Api.InputPeer? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.InputPeer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputChannel.inputChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api9.swift b/submodules/TelegramApi/Sources/Api9.swift index 8d077df130..d67c5a00ae 100644 --- a/submodules/TelegramApi/Sources/Api9.swift +++ b/submodules/TelegramApi/Sources/Api9.swift @@ -1,3 +1,227 @@ +public extension Api { + enum InputBusinessGreetingMessage: TypeConstructorDescription { + case inputBusinessGreetingMessage(shortcutId: Int32, recipients: Api.InputBusinessRecipients, noActivityDays: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + if boxed { + buffer.appendInt32(26528571) + } + serializeInt32(shortcutId, buffer: buffer, boxed: false) + recipients.serialize(buffer, true) + serializeInt32(noActivityDays, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputBusinessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + return ("inputBusinessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) + } + } + + public static func parse_inputBusinessGreetingMessage(_ reader: BufferReader) -> InputBusinessGreetingMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.InputBusinessRecipients? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputBusinessRecipients + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) + } + else { + return nil + } + } + + } +} +public extension Api { + enum InputBusinessIntro: TypeConstructorDescription { + case inputBusinessIntro(flags: Int32, title: String, description: String, sticker: Api.InputDocument?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputBusinessIntro(let flags, let title, let description, let sticker): + if boxed { + buffer.appendInt32(163867085) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputBusinessIntro(let flags, let title, let description, let sticker): + return ("inputBusinessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)]) + } + } + + public static func parse_inputBusinessIntro(_ reader: BufferReader) -> InputBusinessIntro? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: Api.InputDocument? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.InputDocument + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.InputBusinessIntro.inputBusinessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) + } + else { + return nil + } + } + + } +} +public extension Api { + enum InputBusinessRecipients: TypeConstructorDescription { + case inputBusinessRecipients(flags: Int32, users: [Api.InputUser]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputBusinessRecipients(let flags, let users): + if boxed { + buffer.appendInt32(1871393450) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 4) != 0 {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 .inputBusinessRecipients(let flags, let users): + return ("inputBusinessRecipients", [("flags", flags as Any), ("users", users as Any)]) + } + } + + public static func parse_inputBusinessRecipients(_ reader: BufferReader) -> InputBusinessRecipients? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.InputUser]? + if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil + if _c1 && _c2 { + return Api.InputBusinessRecipients.inputBusinessRecipients(flags: _1!, users: _2) + } + else { + return nil + } + } + + } +} +public extension Api { + indirect enum InputChannel: TypeConstructorDescription { + case inputChannel(channelId: Int64, accessHash: Int64) + case inputChannelEmpty + case inputChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputChannel(let channelId, let accessHash): + if boxed { + buffer.appendInt32(-212145112) + } + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + break + case .inputChannelEmpty: + if boxed { + buffer.appendInt32(-292807034) + } + + break + case .inputChannelFromMessage(let peer, let msgId, let channelId): + if boxed { + buffer.appendInt32(1536380829) + } + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt64(channelId, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputChannel(let channelId, let accessHash): + return ("inputChannel", [("channelId", channelId as Any), ("accessHash", accessHash as Any)]) + case .inputChannelEmpty: + return ("inputChannelEmpty", []) + case .inputChannelFromMessage(let peer, let msgId, let channelId): + return ("inputChannelFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("channelId", channelId as Any)]) + } + } + + public static func parse_inputChannel(_ reader: BufferReader) -> InputChannel? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InputChannel.inputChannel(channelId: _1!, accessHash: _2!) + } + else { + return nil + } + } + public static func parse_inputChannelEmpty(_ reader: BufferReader) -> InputChannel? { + return Api.InputChannel.inputChannelEmpty + } + public static func parse_inputChannelFromMessage(_ reader: BufferReader) -> InputChannel? { + var _1: Api.InputPeer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputPeer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.InputChannel.inputChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum InputChatPhoto: TypeConstructorDescription { case inputChatPhoto(id: Api.InputPhoto) @@ -90,6 +314,74 @@ public extension Api { } } +public extension Api { + enum InputChatTheme: TypeConstructorDescription { + case inputChatTheme(emoticon: String) + case inputChatThemeEmpty + case inputChatThemeUniqueGift(slug: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputChatTheme(let emoticon): + if boxed { + buffer.appendInt32(-918689444) + } + serializeString(emoticon, buffer: buffer, boxed: false) + break + case .inputChatThemeEmpty: + if boxed { + buffer.appendInt32(-2094627709) + } + + break + case .inputChatThemeUniqueGift(let slug): + if boxed { + buffer.appendInt32(-2014978076) + } + serializeString(slug, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputChatTheme(let emoticon): + return ("inputChatTheme", [("emoticon", emoticon as Any)]) + case .inputChatThemeEmpty: + return ("inputChatThemeEmpty", []) + case .inputChatThemeUniqueGift(let slug): + return ("inputChatThemeUniqueGift", [("slug", slug as Any)]) + } + } + + public static func parse_inputChatTheme(_ reader: BufferReader) -> InputChatTheme? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputChatTheme.inputChatTheme(emoticon: _1!) + } + else { + return nil + } + } + public static func parse_inputChatThemeEmpty(_ reader: BufferReader) -> InputChatTheme? { + return Api.InputChatTheme.inputChatThemeEmpty + } + public static func parse_inputChatThemeUniqueGift(_ reader: BufferReader) -> InputChatTheme? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputChatTheme.inputChatThemeUniqueGift(slug: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum InputChatlist: TypeConstructorDescription { case inputChatlistDialogFilter(filterId: Int32) diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 22d3b36964..53089efce6 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -853,7 +853,8 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } if let id = state.id as? PeerMessagesMediaPlaylistItemId, let playlistLocation = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation { if type == .music { - if case .custom = playlistLocation { + switch playlistLocation { + case .custom, .savedMusic: let controllerContext: AccountContext if account.id == strongSelf.context.account.id { controllerContext = strongSelf.context @@ -863,7 +864,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: strongSelf.navigationController as? NavigationController) strongSelf.displayNode.view.window?.endEditing(true) strongSelf.present(controller, in: .window(.root)) - } else if case let .messages(chatLocation, _, _) = playlistLocation { + case let .messages(chatLocation, _, _): let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(id.messageId)), count: 60, highlight: true, setupReply: false), id: 0), context: strongSelf.context, chatLocation: chatLocation, subject: nil, chatLocationContextHolder: Atomic(value: nil), tag: .tag(MessageTags.music)) var cancelImpl: (() -> Void)? @@ -915,6 +916,8 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { cancelImpl = { self?.playlistPreloadDisposable?.dispose() } + default: + break } } else { strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: id.messageId.peerId, messageId: id.messageId) diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 51b5870c69..53f6227d60 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -51,6 +51,7 @@ public final class PresentationCallImpl: PresentationCall { private var sessionState: CallSession? private var callContextState: OngoingCallContextState? private var ongoingContext: OngoingCallContext? + private var conferenceCallContext: PresentationGroupCallImpl? private var ongoingContextStateDisposable: Disposable? private var ongoingContextIsFailedDisposable: Disposable? private var ongoingContextIsDroppedDisposable: Disposable? @@ -170,7 +171,6 @@ public final class PresentationCallImpl: PresentationCall { public private(set) var pendingInviteToConferencePeerIds: [(id: EnginePeer.Id, isVideo: Bool)] = [] - private var localVideoEndpointId: String? private var remoteVideoEndpointId: String? private var isMovedToConference: Bool = false @@ -483,7 +483,12 @@ public final class PresentationCallImpl: PresentationCall { self.sessionState = nil self.callContextState = nil let debugLogValue = Promise() - self.ongoingContext?.stop(sendDebugLogs: false, debugLogValue: debugLogValue) + if let conferenceCallContext = self.conferenceCallContext { + let _ = conferenceCallContext.leave(terminateIfPossible: false).startStandalone() + self.conferenceCallContext = nil + } else { + self.ongoingContext?.stop(sendDebugLogs: false, debugLogValue: debugLogValue) + } self.ongoingContext = nil self.ongoingContextStateDisposable?.dispose() self.ongoingContextStateDisposable = nil @@ -552,7 +557,6 @@ public final class PresentationCallImpl: PresentationCall { self.pendingInviteToConferencePeerIds.removeAll() - self.localVideoEndpointId = nil self.remoteVideoEndpointId = nil let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } @@ -598,6 +602,32 @@ public final class PresentationCallImpl: PresentationCall { }) canUpdate = true } + } else if let conferenceCallContext = self.conferenceCallContext { + if self.receptionDisposable == nil, case .active = sessionState.state { + self.reception = 4 + + if self.isOutgoing { + self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date()) + } + + var canUpdate = false + self.receptionDisposable = (conferenceCallContext.signalBars + |> delay(1.0, queue: .mainQueue()) + |> deliverOnMainQueue).start(next: { [weak self] reception in + if let strongSelf = self { + if let sessionState = strongSelf.sessionState { + if canUpdate { + strongSelf.updateSessionState(sessionState: sessionState, callContextState: strongSelf.callContextState, reception: reception, audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf.reception = reception + } + } else { + strongSelf.reception = reception + } + } + }) + canUpdate = true + } } if case .video = sessionState.type { @@ -774,31 +804,31 @@ public final class PresentationCallImpl: PresentationCall { isConference = true } - if let callContextState = callContextState, !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _, _) = sessionState.state { + if let callContextState, !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _, _) = sessionState.state { switch callContextState.state { - case .initializing: - presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) - case .failed: - presentationState = PresentationCallState(state: .terminating(.error(.disconnected)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) - self.callSessionManager.drop(internalId: self.internalId, reason: .disconnect, debugLog: .single(nil)) - case .connected: - let timestamp: Double - if let activeTimestamp = self.activeTimestamp { - timestamp = activeTimestamp - } else { - timestamp = CFAbsoluteTimeGetCurrent() - self.activeTimestamp = timestamp - } - presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) - case .reconnecting: - let timestamp: Double - if let activeTimestamp = self.activeTimestamp { - timestamp = activeTimestamp - } else { - timestamp = CFAbsoluteTimeGetCurrent() - self.activeTimestamp = timestamp - } - presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) + case .initializing: + presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) + case .failed: + presentationState = PresentationCallState(state: .terminating(.error(.disconnected)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) + self.callSessionManager.drop(internalId: self.internalId, reason: .disconnect, debugLog: .single(nil)) + case .connected: + let timestamp: Double + if let activeTimestamp = self.activeTimestamp { + timestamp = activeTimestamp + } else { + timestamp = CFAbsoluteTimeGetCurrent() + self.activeTimestamp = timestamp + } + presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) + case .reconnecting: + let timestamp: Double + if let activeTimestamp = self.activeTimestamp { + timestamp = activeTimestamp + } else { + timestamp = CFAbsoluteTimeGetCurrent() + self.activeTimestamp = timestamp + } + presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) } } else if !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _, _) = sessionState.state { presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel, supportsConferenceCalls: self.supportsConferenceCalls) @@ -1000,70 +1030,247 @@ public final class PresentationCallImpl: PresentationCall { if (self.sharedAudioContext != nil || audioSessionControl != nil), !wasActive || (self.sharedAudioContext == nil && previousControl == nil) { let logName = "\(id.id)_\(id.accessHash)" - let updatedConnections = connections - - let contextAudioSessionActive: Signal - if self.sharedAudioContext != nil { - contextAudioSessionActive = .single(true) - } else { - contextAudioSessionActive = self.audioSessionActive.get() + var inlineConferenceSlug: String? + if let dict = try? JSONSerialization.jsonObject(with: (customParameters ?? "{}").data(using: .utf8)!) as? [String: Any] { + inlineConferenceSlug = dict["inline_conference"] as? String } - let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, callId: id, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: updatedConnections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: contextAudioSessionActive, logName: logName, preferredVideoCodec: self.preferredVideoCodec, audioDevice: self.sharedAudioContext?.audioDevice) - self.ongoingContext = ongoingContext - ongoingContext.setIsMuted(self.isMutedValue) - if let requestedVideoAspect = self.requestedVideoAspect { - ongoingContext.setRequestedVideoAspect(requestedVideoAspect) - } - - self.debugInfoValue.set(ongoingContext.debugInfo()) - - self.ongoingContextStateDisposable = (ongoingContext.state - |> deliverOnMainQueue).start(next: { [weak self] contextState in - if let strongSelf = self { - if let sessionState = strongSelf.sessionState { - strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl) - } else { - strongSelf.callContextState = contextState - } - } - }) - - self.audioLevelDisposable = (ongoingContext.audioLevel - |> deliverOnMainQueue).start(next: { [weak self] level in - if let strongSelf = self { - strongSelf.audioLevelPromise.set(level) - } - }) - - func batteryLevelIsLowSignal() -> Signal { - return Signal { subscriber in - let device = UIDevice.current - device.isBatteryMonitoringEnabled = true - - var previousBatteryLevelIsLow = false - let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: { - let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging - if batteryLevelIsLow != previousBatteryLevelIsLow { - previousBatteryLevelIsLow = batteryLevelIsLow - subscriber.putNext(batteryLevelIsLow) + if let inlineConferenceSlug { + if self.conferenceCallDisposable == nil { + let conferenceCallSignal = self.context.engine.calls.getCurrentGroupCall(reference: .link(slug: inlineConferenceSlug)) + self.conferenceCallDisposable = (conferenceCallSignal + |> deliverOnMainQueue).startStrict(next: { [weak self] groupCall in + guard let self else { + return + } + let keyPair: TelegramKeyPair? = TelegramE2EEncryptionProviderImpl.shared.generateKeyPair() + guard let keyPair, let groupCall else { + self.sessionStateDisposable?.dispose() + self.updateSessionState( + sessionState: CallSession( + id: self.internalId, + stableId: self.conferenceStableId, + isOutgoing: false, + type: self.isVideo ? .video : .audio, + state: .terminated(id: nil, reason: .error(.generic), options: CallTerminationOptions()), + isVideoPossible: true + ), + callContextState: nil, + reception: nil, + audioSessionControl: self.audioSessionControl + ) + return + } + + let conferenceCallContext = PresentationGroupCallImpl( + accountContext: self.context, + audioSession: self.audioSession, + callKitIntegration: self.callKitIntegration, + getDeviceAccessData: self.getDeviceAccessData, + initialCall: (EngineGroupCallDescription( + id: groupCall.info.id, + accessHash: groupCall.info.accessHash, + title: nil, + scheduleTimestamp: nil, + subscribedToScheduled: false, + isStream: false + ), .link(slug: inlineConferenceSlug)), + internalId: self.internalId, + peerId: nil, + isChannel: false, + invite: nil, + joinAsPeerId: nil, + isStream: false, + keyPair: keyPair, + conferenceSourceId: self.internalId, + isConference: true, + beginWithVideo: false, + sharedAudioContext: self.sharedAudioContext + ) + self.conferenceCallContext = conferenceCallContext + + conferenceCallContext.setIsMuted(action: self.isMutedValue ? .muted(isPushToTalkActive: false) : .unmuted) + if let videoCapturer = self.videoCapturer { + conferenceCallContext.requestVideo(capturer: videoCapturer) + } + + self.ongoingContextStateDisposable = (combineLatest(queue: .mainQueue(), + conferenceCallContext.state, + conferenceCallContext.members + ) |> deliverOnMainQueue).start(next: { [weak self] contextState, contextMembers in + guard let self else { + return + } + + let mappedState: OngoingCallContextState.State + if contextState.networkState == .connected { + mappedState = .connected + } else { + mappedState = .reconnecting + } + + var localVideoState: OngoingCallContextState.VideoState = .inactive + var remoteVideoState: OngoingCallContextState.RemoteVideoState = .inactive + var remoteVideoEndpointId: String? + var remoteVideoDescription: PresentationGroupCallRequestedVideo? + var remoteAudioState: OngoingCallContextState.RemoteAudioState = .active + + if let contextMembers { + for participant in contextMembers.participants { + if participant.peer?.id == self.context.account.peerId { + if participant.videoDescription != nil { + localVideoState = .active + } + } else if participant.peer?.id == self.peerId { + remoteVideoDescription = participant.requestedVideoChannel(minQuality: .thumbnail, maxQuality: .full) + if remoteVideoDescription != nil { + remoteVideoState = .active + remoteVideoEndpointId = remoteVideoDescription?.endpointId + } + remoteAudioState = participant.muteState == nil ? .active : .muted + } + } + } + self.remoteVideoEndpointId = remoteVideoEndpointId + var requestedVideoList: [PresentationGroupCallRequestedVideo] = [] + if let remoteVideoDescription { + requestedVideoList.append(remoteVideoDescription) + } + self.conferenceCallContext?.setRequestedVideoList(items: requestedVideoList) + + let mappedContextState = OngoingCallContextState( + state: mappedState, + videoState: localVideoState, + remoteVideoState: remoteVideoState, + remoteAudioState: remoteAudioState, + remoteBatteryLevel: .normal + ) + + if let sessionState = self.sessionState { + self.updateSessionState(sessionState: sessionState, callContextState: mappedContextState, reception: self.reception, audioSessionControl: self.audioSessionControl) + } else { + self.callContextState = mappedContextState + } + }) + + self.audioLevelDisposable = (conferenceCallContext.audioLevels + |> deliverOnMainQueue).start(next: { [weak self] levels in + guard let self else { + return + } + var level: Float = 0.0 + for (peerId, _, levelValue, _) in levels { + if peerId == self.peerId { + level = levelValue + break + } + } + self.audioLevelPromise.set(level) + }) + + func batteryLevelIsLowSignal() -> Signal { + return Signal { subscriber in + let device = UIDevice.current + device.isBatteryMonitoringEnabled = true + + var previousBatteryLevelIsLow = false + let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: { + let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging + if batteryLevelIsLow != previousBatteryLevelIsLow { + previousBatteryLevelIsLow = batteryLevelIsLow + subscriber.putNext(batteryLevelIsLow) + } + }, queue: Queue.mainQueue()) + timer.start() + + return ActionDisposable { + device.isBatteryMonitoringEnabled = false + timer.invalidate() + } + } + } + }, error: { [weak self] _ in + guard let self else { + return + } + self.sessionStateDisposable?.dispose() + self.updateSessionState(sessionState: CallSession( + id: self.internalId, + stableId: self.conferenceStableId, + isOutgoing: false, + type: .audio, + state: .terminated(id: nil, reason: .error(.generic), options: CallTerminationOptions()), + isVideoPossible: true + ), + callContextState: nil, reception: nil, audioSessionControl: self.audioSessionControl) + }) + } + } else { + let updatedConnections = connections + + let contextAudioSessionActive: Signal + if self.sharedAudioContext != nil { + contextAudioSessionActive = .single(true) + } else { + contextAudioSessionActive = self.audioSessionActive.get() + } + + let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, callId: id, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: updatedConnections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: contextAudioSessionActive, logName: logName, preferredVideoCodec: self.preferredVideoCodec, audioDevice: self.sharedAudioContext?.audioDevice) + self.ongoingContext = ongoingContext + ongoingContext.setIsMuted(self.isMutedValue) + if let requestedVideoAspect = self.requestedVideoAspect { + ongoingContext.setRequestedVideoAspect(requestedVideoAspect) + } + + self.debugInfoValue.set(ongoingContext.debugInfo()) + + self.ongoingContextStateDisposable = (ongoingContext.state + |> deliverOnMainQueue).start(next: { [weak self] contextState in + if let strongSelf = self { + if let sessionState = strongSelf.sessionState { + strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf.callContextState = contextState + } + } + }) + + self.audioLevelDisposable = (ongoingContext.audioLevel + |> deliverOnMainQueue).start(next: { [weak self] level in + if let strongSelf = self { + strongSelf.audioLevelPromise.set(level) + } + }) + + func batteryLevelIsLowSignal() -> Signal { + return Signal { subscriber in + let device = UIDevice.current + device.isBatteryMonitoringEnabled = true + + var previousBatteryLevelIsLow = false + let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: { + let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging + if batteryLevelIsLow != previousBatteryLevelIsLow { + previousBatteryLevelIsLow = batteryLevelIsLow + subscriber.putNext(batteryLevelIsLow) + } + }, queue: Queue.mainQueue()) + timer.start() + + return ActionDisposable { + device.isBatteryMonitoringEnabled = false + timer.invalidate() } - }, queue: Queue.mainQueue()) - timer.start() - - return ActionDisposable { - device.isBatteryMonitoringEnabled = false - timer.invalidate() } } + + self.batteryLevelDisposable = (batteryLevelIsLowSignal() + |> deliverOnMainQueue).start(next: { [weak self] batteryLevelIsLow in + if let strongSelf = self, let ongoingContext = strongSelf.ongoingContext { + ongoingContext.setIsLowBatteryLevel(batteryLevelIsLow) + } + }) } - - self.batteryLevelDisposable = (batteryLevelIsLowSignal() - |> deliverOnMainQueue).start(next: { [weak self] batteryLevelIsLow in - if let strongSelf = self, let ongoingContext = strongSelf.ongoingContext { - ongoingContext.setIsLowBatteryLevel(batteryLevelIsLow) - } - }) } } case .switchedToConference: @@ -1072,7 +1279,12 @@ public final class PresentationCallImpl: PresentationCall { self.audioSessionShouldBeActive.set(true) if wasActive { let debugLogValue = Promise() - self.ongoingContext?.stop(sendDebugLogs: options.contains(.sendDebugLogs), debugLogValue: debugLogValue) + if let conferenceCallContext = self.conferenceCallContext { + debugLogValue.set(conferenceCallContext.debugLog.get()) + let _ = conferenceCallContext.leave(terminateIfPossible: false).startStandalone() + } else { + self.ongoingContext?.stop(sendDebugLogs: options.contains(.sendDebugLogs), debugLogValue: debugLogValue) + } } case .dropping: break @@ -1080,7 +1292,12 @@ public final class PresentationCallImpl: PresentationCall { self.audioSessionShouldBeActive.set(false) if wasActive { let debugLogValue = Promise() - self.ongoingContext?.stop(debugLogValue: debugLogValue) + if let conferenceCallContext = self.conferenceCallContext { + debugLogValue.set(conferenceCallContext.debugLog.get()) + let _ = conferenceCallContext.leave(terminateIfPossible: false).startStandalone() + } else { + self.ongoingContext?.stop(debugLogValue: debugLogValue) + } } } var terminating = false @@ -1301,7 +1518,12 @@ public final class PresentationCallImpl: PresentationCall { let debugLogValue = Promise() self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get()) - self.ongoingContext?.stop(debugLogValue: debugLogValue) + if let conferenceCallContext = self.conferenceCallContext { + debugLogValue.set(conferenceCallContext.debugLog.get()) + let _ = conferenceCallContext.leave(terminateIfPossible: false).startStandalone() + } else { + self.ongoingContext?.stop(debugLogValue: debugLogValue) + } return self.hungUpPromise.get() } @@ -1312,7 +1534,12 @@ public final class PresentationCallImpl: PresentationCall { } self.callSessionManager.drop(internalId: self.internalId, reason: .busy, debugLog: .single(nil)) let debugLog = Promise() - self.ongoingContext?.stop(debugLogValue: debugLog) + if let conferenceCallContext = self.conferenceCallContext { + debugLog.set(conferenceCallContext.debugLog.get()) + let _ = conferenceCallContext.leave(terminateIfPossible: false).startStandalone() + } else { + self.ongoingContext?.stop(debugLogValue: debugLog) + } } public func toggleIsMuted() { @@ -1328,7 +1555,11 @@ public final class PresentationCallImpl: PresentationCall { } self.isMutedValue = value self.isMutedPromise.set(self.isMutedValue) - self.ongoingContext?.setIsMuted(self.isMutedValue) + if let conferenceCallContext = self.conferenceCallContext { + conferenceCallContext.setIsMuted(action: value ? .muted(isPushToTalkActive: false) : .unmuted) + } else { + self.ongoingContext?.setIsMuted(self.isMutedValue) + } } public func requestVideo() { @@ -1340,7 +1571,9 @@ public final class PresentationCallImpl: PresentationCall { self.videoCapturer = videoCapturer } if let videoCapturer = self.videoCapturer { - if let ongoingContext = self.ongoingContext { + if let conferenceCallContext = self.conferenceCallContext { + conferenceCallContext.requestVideo(capturer: videoCapturer) + } else if let ongoingContext = self.ongoingContext { ongoingContext.requestVideo(videoCapturer) } } @@ -1354,7 +1587,9 @@ public final class PresentationCallImpl: PresentationCall { self.videoCapturer = capturer } if let videoCapturer = self.videoCapturer { - if let ongoingContext = self.ongoingContext { + if let conferenceCallContext = self.conferenceCallContext { + conferenceCallContext.requestVideo(capturer: videoCapturer) + } else if let ongoingContext = self.ongoingContext { ongoingContext.requestVideo(videoCapturer) } } @@ -1374,7 +1609,9 @@ public final class PresentationCallImpl: PresentationCall { } if let _ = self.videoCapturer { self.videoCapturer = nil - if let ongoingContext = self.ongoingContext { + if let conferenceCallContext = self.conferenceCallContext { + conferenceCallContext.disableVideo() + } else if let ongoingContext = self.ongoingContext { ongoingContext.disableVideo() } } @@ -1404,7 +1641,11 @@ public final class PresentationCallImpl: PresentationCall { guard let strongSelf = self else { return } - strongSelf.ongoingContext?.addExternalAudioData(data: data) + if let conferenceCallContext = strongSelf.conferenceCallContext { + conferenceCallContext.genericCallContext?.addExternalAudioData(data: data) + } else { + strongSelf.ongoingContext?.addExternalAudioData(data: data) + } })) self.screencastStateDisposable.set((screencastBufferServerContext.isActive |> distinctUntilChanged @@ -1428,7 +1669,9 @@ public final class PresentationCallImpl: PresentationCall { if let screencastCapturer = self.screencastCapturer { self.isScreencastActive = true - if let ongoingContext = self.ongoingContext { + if let conferenceCallContext = self.conferenceCallContext { + conferenceCallContext.requestVideo(capturer: screencastCapturer) + } else if let ongoingContext = self.ongoingContext { ongoingContext.requestVideo(screencastCapturer) } } @@ -1443,6 +1686,7 @@ public final class PresentationCallImpl: PresentationCall { self.videoCapturer = nil } self.isScreencastActive = false + self.conferenceCallContext?.disableVideo() self.ongoingContext?.disableVideo() self.conferenceCallImpl?.disableVideo() if reset { @@ -1524,7 +1768,9 @@ public final class PresentationCallImpl: PresentationCall { return nil } if isIncoming { - if let ongoingContext = self.ongoingContext { + if let conferenceCallContext = self.conferenceCallContext { + return conferenceCallContext.video(endpointId: self.remoteVideoEndpointId ?? "any_remote") + } else if let ongoingContext = self.ongoingContext { return ongoingContext.video(isIncoming: isIncoming) } else { return nil @@ -1646,7 +1892,12 @@ public final class PresentationCallImpl: PresentationCall { } func deactivateIncomingAudio() { - self.ongoingContext?.deactivateIncomingAudio() + if let conferenceCallContext = self.conferenceCallContext { + let _ = conferenceCallContext + //TODO:release + } else { + self.ongoingContext?.deactivateIncomingAudio() + } } } diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 0064ab36ab..5d79763057 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -40,13 +40,13 @@ private extension PresentationGroupCallState { } } -private enum CurrentImpl { +enum CurrentImpl { case call(OngoingGroupCallContext) case mediaStream(WrappedMediaStreamingContext) case externalMediaStream(DirectMediaStreamingContext) } -private extension CurrentImpl { +extension CurrentImpl { var joinPayload: Signal<(String, UInt32), NoError> { switch self { case let .call(callContext): @@ -555,7 +555,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private var currentLocalSsrc: UInt32? private var currentLocalEndpointId: String? - private var genericCallContext: CurrentImpl? + private(set) var genericCallContext: CurrentImpl? private var currentConnectionMode: OngoingGroupCallContext.ConnectionMode = .none private var didInitializeConnectionMode: Bool = false diff --git a/submodules/TelegramCore/FlatSerialization/Models/PartialMediaReference.fbs b/submodules/TelegramCore/FlatSerialization/Models/PartialMediaReference.fbs index 65fb77a3ba..852e58faf1 100644 --- a/submodules/TelegramCore/FlatSerialization/Models/PartialMediaReference.fbs +++ b/submodules/TelegramCore/FlatSerialization/Models/PartialMediaReference.fbs @@ -41,13 +41,18 @@ table PartialMediaReference_SavedSticker { table PartialMediaReference_RecentSticker { } +table PartialMediaReference_SavedMusic { + peer:PeerReference (id: 0); +} + union PartialMediaReference_Value { PartialMediaReference_Message, PartialMediaReference_WebPage, PartialMediaReference_StickerPack, PartialMediaReference_SavedGif, PartialMediaReference_SavedSticker, - PartialMediaReference_RecentSticker + PartialMediaReference_RecentSticker, + PartialMediaReference_SavedMusic } table PartialMediaReference { diff --git a/submodules/TelegramCore/FlatSerialization/Models/TelegramChannel.fbs b/submodules/TelegramCore/FlatSerialization/Models/TelegramChannel.fbs index 8440b863e9..5665059453 100644 --- a/submodules/TelegramCore/FlatSerialization/Models/TelegramChannel.fbs +++ b/submodules/TelegramCore/FlatSerialization/Models/TelegramChannel.fbs @@ -56,6 +56,7 @@ table TelegramChannel { verificationIconFileId:int64 (id: 23); sendPaidMessageStars:StarsAmount (id: 24); linkedMonoforumId:PeerId (id: 25); + linkedBotId:PeerId (id: 26); } root_type TelegramChannel; diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 4836e5ece5..e17ded2bde 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -127,7 +127,7 @@ public class UnauthorizedAccount { if let nextType = nextType { parsedNextType = AuthorizationCodeNextType(apiType: nextType) } - if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(phoneNumber, _, _, syncContacts) = state.contents { + if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(phoneNumber, _, _, _, _, syncContacts) = state.contents { transaction.setState(UnauthorizedAccountState(isTestingEnvironment: testingEnvironment, masterDatacenterId: masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false))) } }).start() @@ -136,7 +136,7 @@ public class UnauthorizedAccount { case let .authorization(_, _, _, futureAuthToken, user): let _ = postbox.transaction({ [weak self] transaction in var syncContacts = true - if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(_, _, _, syncContactsValue) = state.contents { + if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(_, _, _, _, _, syncContactsValue) = state.contents { syncContacts = syncContactsValue } @@ -162,7 +162,7 @@ public class UnauthorizedAccount { case let .authorizationSignUpRequired(_, termsOfService): let _ = postbox.transaction({ [weak self] transaction in if let self { - if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(number, codeHash, _, syncContacts) = state.contents { + if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(number, codeHash, _, _, _, syncContacts) = state.contents { let _ = beginSignUp( account: self, data: AuthorizationSignUpData( diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 209de31fba..756178bc4a 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -93,6 +93,7 @@ enum AccountStateMutationOperation { case AddSecretMessages([Api.EncryptedMessage]) case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32) case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?) + case AddPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId, id: Int64, timestamp: Int32, peerId: PeerId, text: String, entities: [MessageTextEntity]) case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation) case UpdatePinnedSavedItemIds(AccountStateUpdatePinnedItemIdsOperation) case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool) @@ -592,6 +593,10 @@ struct AccountMutableState { self.addOperation(.AddPeerInputActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity)) } + mutating func addPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId, id: Int64, timestamp: Int32, peerId: PeerId, text: String, entities: [MessageTextEntity]) { + self.addOperation(.AddPeerLiveTypingDraftUpdate(peerAndThreadId: peerAndThreadId, id: id, timestamp: timestamp, peerId: peerId, text: text, entities: entities)) + } + mutating func addUpdatePinnedItemIds(groupId: PeerGroupId, operation: AccountStateUpdatePinnedItemIdsOperation) { self.addOperation(.UpdatePinnedItemIds(groupId, operation)) } @@ -714,7 +719,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException: break case let .AddMessages(messages, location): for message in messages { diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index 87373e26a7..e9a1f751a5 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -188,7 +188,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { } } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) }, linkedMonoforumId: linkedMonoforumId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) }, linkedMonoforumId: linkedMonoforumId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, linkedBotId: nil) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -197,7 +197,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { info = .broadcast(TelegramChannelBroadcastInfo(flags: [])) } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil) } } @@ -271,7 +271,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { let linkedMonoforumId = linkedMonoforumIdValue.flatMap({ PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }) ?? lhs.linkedMonoforumId - return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } ?? lhs.sendPaidMessageStars, linkedMonoforumId: linkedMonoforumId) + return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } ?? lhs.sendPaidMessageStars, linkedMonoforumId: linkedMonoforumId, linkedBotId: nil) } else { return parseTelegramGroupOrChannel(chat: rhs) } @@ -326,7 +326,8 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden let linkedMonoforumId = rhs.linkedMonoforumId ?? lhs.linkedMonoforumId + let linkedBotId = rhs.linkedBotId ?? lhs.linkedBotId - return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel, subscriptionUntilDate: rhs.subscriptionUntilDate, verificationIconFileId: rhs.verificationIconFileId, sendPaidMessageStars: rhs.sendPaidMessageStars, linkedMonoforumId: linkedMonoforumId) + return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel, subscriptionUntilDate: rhs.subscriptionUntilDate, verificationIconFileId: rhs.verificationIconFileId, sendPaidMessageStars: rhs.sendPaidMessageStars, linkedMonoforumId: linkedMonoforumId, linkedBotId: linkedBotId) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 7cdd5ca5a6..0abf420a65 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -1071,7 +1071,7 @@ extension StoreMessage { storeFlags.insert(.CanBeGroupedIntoFeed) - self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias) + self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), customStableId: nil, globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias) case .messageEmpty: return nil case let .messageService(flags, id, fromId, chatPeerId, savedPeerId, replyTo, date, action, reactions, ttlPeriod): @@ -1210,7 +1210,7 @@ extension StoreMessage { storeFlags.insert(.ReactionsArePossible) } - self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: nil, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: attributes, media: media) + self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: attributes, media: media) } } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index e29a337285..d90a71b734 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -101,8 +101,12 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe case .inputGroupCallSlug, .inputGroupCallInviteMessage: return nil } - case let .messageActionSetChatTheme(emoji): - return TelegramMediaAction(action: .setChatTheme(emoji: emoji)) + case let .messageActionSetChatTheme(chatTheme): + if let chatTheme = ChatTheme(apiChatTheme: chatTheme) { + return TelegramMediaAction(action: .setChatTheme(chatTheme: chatTheme)) + } else { + return nil + } case .messageActionChatJoinedByRequest: return TelegramMediaAction(action: .joinedByRequest) case let .messageActionWebViewDataSentMe(text, _), let .messageActionWebViewDataSent(text): @@ -177,7 +181,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe return TelegramMediaAction(action: .paymentRefunded(peerId: peer.peerId, currency: currency, totalAmount: totalAmount, payload: payload?.makeData(), transactionId: transactionId)) case let .messageActionPrizeStars(flags, stars, transactionId, boostPeer, giveawayMsgId): return TelegramMediaAction(action: .prizeStars(amount: stars, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer.peerId, transactionId: transactionId, giveawayMessageId: MessageId(peerId: boostPeer.peerId, namespace: Namespaces.Message.Cloud, id: giveawayMsgId))) - case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeMessageId, upgradeStars, fromId, peer, savedId): + case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeMessageId, upgradeStars, fromId, peer, savedId, prepaidUpgradeHash, giftMessageId): let text: String? let entities: [MessageTextEntity]? switch message { @@ -191,12 +195,12 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe guard let gift = StarGift(apiStarGift: apiGift) else { return nil } - return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId)) + return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0, isPrepaidUpgrade: (flags & (1 << 13)) != 0, upgradeMessageId: upgradeMessageId, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, prepaidUpgradeHash: prepaidUpgradeHash, giftMessageId: giftMessageId, upgradeSeparate: (flags & (1 << 16)) != 0)) case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars, fromId, peer, savedId, resaleAmount, canTransferDate, canResaleDate): guard let gift = StarGift(apiStarGift: apiGift) else { return nil } - return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate)) + return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, isPrepaidUpgrade: (flags & (1 << 11)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate)) case let .messageActionPaidMessagesRefunded(count, stars): return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars)) case let .messageActionPaidMessagesPrice(flags, stars): diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift index e56be3f10a..e95fc8896f 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift @@ -118,6 +118,9 @@ extension TelegramUser { if (flags2 & (1 << 13)) != 0 { botFlags.insert(.hasWebApp) } + if (flags2 & (1 << 16)) != 0 { + botFlags.insert(.hasForum) + } botInfo = BotUserInfo(flags: botFlags, inlinePlaceholder: botInlinePlaceholder) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TypingDraftMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TypingDraftMessageAttribute.swift new file mode 100644 index 0000000000..23a5ef6e20 --- /dev/null +++ b/submodules/TelegramCore/Sources/ApiUtils/TypingDraftMessageAttribute.swift @@ -0,0 +1,15 @@ +import Foundation +import Postbox + +public final class TypingDraftMessageAttribute: MessageAttribute { + public init() { + } + + public init(decoder: PostboxDecoder) { + preconditionFailure() + } + + public func encode(_ encoder: PostboxEncoder) { + preconditionFailure() + } +} diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 836687ce7f..a146932d6f 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -518,8 +518,8 @@ private func internalResendAuthorizationCode(accountManager: AccountManager Signal { - return account.postbox.transaction { transaction -> Peer? in + return _internal_createForumChannelTopic(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, title: title, iconColor: iconColor, iconFileId: iconFileId) +} + +func _internal_createForumChannelTopic(postbox: Postbox, network: Network, stateManager: AccountStateManager, accountPeerId: PeerId, peerId: PeerId, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal { + return postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) } |> castError(CreateForumChannelTopicError.self) @@ -218,7 +222,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: flags |= (1 << 3) } flags |= (1 << 0) - return account.network.request(Api.functions.channels.createForumTopic( + return network.request(Api.functions.channels.createForumTopic( flags: flags, channel: inputChannel, title: title, @@ -231,14 +235,14 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: return .generic } |> mapToSignal { result -> Signal in - account.stateManager.addUpdates(result) + stateManager.addUpdates(result) var topicId: Int64? topicId = nil for update in result.allUpdates { switch update { case let .updateNewChannelMessage(message, _, _): - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) { if case let .Id(id) = message.id { topicId = Int64(id.id) } @@ -249,12 +253,12 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: } if let topicId { - return account.postbox.transaction { transaction -> Void in + return postbox.transaction { transaction -> Void in transaction.removeHole(peerId: peerId, threadId: topicId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1)) } |> castError(CreateForumChannelTopicError.self) |> mapToSignal { _ -> Signal in - return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)]) + return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: .network(network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)]) |> castError(CreateForumChannelTopicError.self) |> map { _ -> Int64 in return topicId diff --git a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift index 2cc69b8557..40fd07692b 100644 --- a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift @@ -334,6 +334,7 @@ private enum MediaReferenceRevalidationKey: Hashable { case customEmoji(fileId: Int64) case story(peer: PeerReference, id: Int32) case starsTransaction(transaction: StarsTransactionReference) + case savedMusic(peer: PeerReference, fileId: Int64) } private final class MediaReferenceRevalidationItemContext { @@ -794,6 +795,33 @@ final class MediaReferenceRevalidationContext { } } + func savedMusic(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, peer: PeerReference, media: Media) -> Signal { + guard let file = media as? TelegramMediaFile else { + return .fail(.generic) + } + return self.genericItem(key: .savedMusic(peer: peer, fileId: file.fileId.id), background: background, request: { next, error in + return (_internal_getSavedMusicById(postbox: postbox, network: network, peer: peer, file: file) + |> castError(RevalidateMediaReferenceError.self) + |> mapToSignal { result -> Signal in + if let result { + return .single(result) + } else { + return .fail(.generic) + } + }).start(next: { value in + next(value) + }, error: { _ in + error(.generic) + }) + }) |> mapToSignal { next -> Signal in + if let next = next as? TelegramMediaFile { + return .single(next) + } else { + return .fail(.generic) + } + } + } + func notificationSoundList(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> { return self.genericItem(key: .notificationSoundList, background: background, request: { next, error in return (requestNotificationSoundList(network: network, hash: 0) @@ -993,6 +1021,14 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n } return .fail(.generic) } + case let .savedMusic(peer, media): + return revalidationContext.savedMusic(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer, media: media) + |> mapToSignal { result -> Signal in + if let updatedResource = findUpdatedMediaResource(media: result, previousMedia: media, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + return .fail(.generic) + } case let .standalone(media): if let file = media as? TelegramMediaFile { for attribute in file.attributes { diff --git a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift index 6f944d4769..ed3d1028f1 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift @@ -388,6 +388,8 @@ private enum MultipartFetchSource { |> mapError { error -> MultipartFetchDownloadError in if error.errorDescription == "WEBFILE_NOT_AVAILABLE" { return .webfileNotAvailable + } else if error.errorDescription.hasPrefix("FILE_REFERENCE_EXPIRED") { + return .revalidateMediaReference } return .fatal } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index c33c4e39f8..2e9687ce88 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -566,7 +566,12 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, if transformedMedia { infoFlags.insert(.transformedMedia) } - attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: infoFlags, acknowledged: false, correlationId: message.correlationId, bubbleUpEmojiOrStickersets: message.bubbleUpEmojiOrStickersets)) + + var partialReference: PartialMediaReference? + if case let .message(_, _, _, mediaReference, _, _, _, _, _, _) = message { + partialReference = mediaReference?.partial + } + attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: infoFlags, acknowledged: false, correlationId: message.correlationId, bubbleUpEmojiOrStickersets: message.bubbleUpEmojiOrStickersets, partialReference: partialReference)) globallyUniqueIds.append(randomId) switch message { @@ -828,7 +833,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, threadId = 1 } - storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList)) + storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, customStableId: nil, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList)) case let .forward(source, threadId, grouping, requestedAttributes, _): let sourceMessage = transaction.getMessage(source) if let sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] { @@ -1073,7 +1078,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, threadId = 1 } - storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList)) + storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, customStableId: nil, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList)) } } } diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 5ca32413ab..1940b8d05e 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -70,6 +70,8 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po var contextResult: OutgoingChatContextResultMessageAttribute? var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute? var autoclearMessageAttribute: AutoclearTimeoutMessageAttribute? + var forwardInfo: ForwardSourceInfoAttribute? + var explicitPartialReference: PartialMediaReference? for attribute in attributes { if let attribute = attribute as? OutgoingChatContextResultMessageAttribute { if peerId.namespace != Namespaces.Peer.SecretChat { @@ -79,18 +81,17 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po autoremoveMessageAttribute = attribute } else if let attribute = attribute as? AutoclearTimeoutMessageAttribute { autoclearMessageAttribute = attribute - } - } - - var forwardInfo: ForwardSourceInfoAttribute? - for attribute in attributes { - if let attribute = attribute as? ForwardSourceInfoAttribute { + } else if let attribute = attribute as? OutgoingMessageInfoAttribute { + if let partialReference = attribute.partialReference { + explicitPartialReference = partialReference + } + } else if let attribute = attribute as? ForwardSourceInfoAttribute { if peerId.namespace != Namespaces.Peer.SecretChat { forwardInfo = attribute } } } - + if let media = media.first as? TelegramMediaAction, media.action == .historyScreenshot { return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .messageScreenshot, reuploadInfo: nil, cacheReferenceKey: nil)), .none) } else if let forwardInfo = forwardInfo { @@ -121,14 +122,14 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po return .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaWebPage(flags: flags, url: content.url), text), reuploadInfo: nil, cacheReferenceKey: nil)) } |> castError(PendingMessageUploadError.self), .text) - } else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, forceNoBigParts: forceNoBigParts, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes, mediaReference: mediaReference) { + } else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, forceNoBigParts: forceNoBigParts, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes, mediaReference: mediaReference, explicitPartialReference: explicitPartialReference) { return .signal(mediaResult, .media) } else { return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text) } } -func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute], mediaReference: AnyMediaReference?) -> Signal? { +func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute], mediaReference: AnyMediaReference?, explicitPartialReference: PartialMediaReference?) -> Signal? { if let paidContent = media as? TelegramMediaPaidContent { var signals: [Signal] = [] var mediaIds: [MediaId] = [] @@ -217,6 +218,8 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post return .standalone(media: file) } } + } else if let explicitPartialReference { + finalMediaReference = .single(explicitPartialReference.mediaReference(file)) } else { finalMediaReference = .single(.savedGif(media: file)) } @@ -580,11 +583,11 @@ if "".isEmpty { let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia])) } else { - updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [], partialReference: nil)) } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) }) } return .done(media) @@ -1031,11 +1034,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia])) } else { - updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [], partialReference: nil)) } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) }) } return .done(media) diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift index fa1f8033ce..5c5f8a7cc9 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingPeerMediaUploadManager.swift @@ -120,7 +120,7 @@ private func generatePeerMediaMessage(network: Network, accountPeerId: EnginePee flags.insert(.Sending) var attributes: [MessageAttribute] = [] - attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: [], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: [], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: [], partialReference: nil)) var media: [Media] = [] switch content { @@ -128,7 +128,7 @@ private func generatePeerMediaMessage(network: Network, accountPeerId: EnginePee media.append(TelegramMediaAction(action: .setChatWallpaper(wallpaper: wallpaper, forBoth: forBoth))) } - return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media) + return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media) } private final class PendingPeerMediaUploadContext { diff --git a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift index 6b993d8911..7316f7234f 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift @@ -66,7 +66,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, if let invertMediaAttribute { attributes.append(invertMediaAttribute) } - return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, forceNoBigParts: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: attributes, mediaReference: nil) + return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, forceNoBigParts: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: attributes, mediaReference: nil, explicitPartialReference: nil) } if let todo = media.media as? TelegramMediaTodo { var flags: Int32 = 0 @@ -358,7 +358,7 @@ func _internal_requestEditLiveLocation(postbox: Postbox, network: Network, state } var updatedLocalTags = currentMessage.localTags updatedLocalTags.remove(.OutgoingLiveLocation) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: updatedLocalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: updatedLocalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } else { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index cd8759bde5..546e5700b0 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1193,7 +1193,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: } } - let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias) + let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias) updatedState.addMessages([message], location: .UpperHistoryBlock) } } @@ -1470,15 +1470,22 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)) let threadId = topMsgId.flatMap { Int64($0) } - let activity = PeerInputActivity(apiType: type, peerId: nil, timestamp: date) - var category: PeerActivitySpace.Category = .global - if case .speakingInGroupCall = activity { - category = .voiceChat - } else if let threadId = threadId { - category = .thread(threadId) + /*if case let .sendMessageTextDraftAction(randomId, text) = type { + switch text { + case let .textWithEntities(text, entities): + updatedState.addPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId(peerId: channelPeerId, threadId: threadId), id: randomId, timestamp: date, peerId: userId.peerId, text: text, entities: messageTextEntitiesFromApiEntities(entities)) + } + } else*/ do { + let activity = PeerInputActivity(apiType: type, peerId: nil, timestamp: date) + var category: PeerActivitySpace.Category = .global + if case .speakingInGroupCall = activity { + category = .voiceChat + } else if let threadId = threadId { + category = .thread(threadId) + } + + updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: userId.peerId, activity: activity) } - - updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: userId.peerId, activity: activity) } case let .updateEncryptedChatTyping(chatId): if let date = updatesDate, date + 60 > serverTime { @@ -3583,7 +3590,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddQuickReplyMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -3767,6 +3774,31 @@ func replayFinalState( var addedOperationIncomingMessageIds: [MessageId] = [] var addedConferenceInvitationMessagesIds: [MessageId] = [] + + enum LiveTypingDraftUpdate { + struct Update { + var id: Int64 + var threadId: Int64? + var authorId: PeerId + var timestamp: Int32 + var text: String + var entities: [MessageTextEntity] + + init(id: Int64, threadId: Int64?, authorId: PeerId, timestamp: Int32, text: String, entities: [MessageTextEntity]) { + self.id = id + self.threadId = threadId + self.authorId = authorId + self.timestamp = timestamp + self.text = text + self.entities = entities + } + } + + case update(Update) + case cancel + } + + var liveTypingDraftUpdates: [PeerAndThreadId: LiveTypingDraftUpdate] = [:] for operation in finalState.state.operations { switch operation { @@ -3965,6 +3997,27 @@ func replayFinalState( } } } + + var messages = messages + + if case .UpperHistoryBlock = location { + for i in 0 ..< messages.count { + let message = messages[i] + let chatPeerId = message.id.peerId + let key = PeerAndThreadId(peerId: chatPeerId, threadId: message.threadId) + let allKey = PeerAndThreadId(peerId: chatPeerId, threadId: nil) + + if liveTypingDraftUpdates[key] != nil { + liveTypingDraftUpdates[key] = .cancel + liveTypingDraftUpdates[allKey] = .cancel + } else if let currentDraft = transaction.getCurrentTypingDraft(location: key) { + liveTypingDraftUpdates[key] = .cancel + liveTypingDraftUpdates[allKey] = .cancel + messages[i] = messages[i].withUpdatedCustomStableId(currentDraft.stableId) + } + } + } + let _ = transaction.addMessages(messages, location: location) if case .UpperHistoryBlock = location { for message in messages { @@ -3982,6 +4035,7 @@ func replayFinalState( } else { updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, category: .thread(threadId))]![authorId] = activityValue } + } } @@ -4008,7 +4062,7 @@ func replayFinalState( }) } switch action.action { - case let .setChatTheme(emoticon): + case let .setChatTheme(chatTheme): transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in var current = current if current == nil { @@ -4021,11 +4075,11 @@ func replayFinalState( } } if let cachedData = current as? CachedUserData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + return cachedData.withUpdatedChatTheme(!chatTheme.isEmpty ? chatTheme : nil) } else if let cachedData = current as? CachedGroupData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + return cachedData.withUpdatedChatTheme(!chatTheme.isEmpty ? chatTheme : nil) } else if let cachedData = current as? CachedChannelData { - return cachedData.withUpdatedThemeEmoticon(!emoticon.isEmpty ? emoticon : nil) + return cachedData.withUpdatedChatTheme(!chatTheme.isEmpty ? chatTheme : nil) } else { return current } @@ -4317,7 +4371,7 @@ func replayFinalState( return .skip } attributes[j] = ReplyThreadMessageAttribute(count: attribute.count, latestUsers: attribute.latestUsers, commentsPeerId: attribute.commentsPeerId, maxMessageId: attribute.maxMessageId, maxReadMessageId: readMaxId) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) } } return .skip @@ -4578,7 +4632,7 @@ func replayFinalState( return .skip } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } case let .MergePeerPresences(statuses, explicit): @@ -4622,6 +4676,25 @@ func replayFinalState( } else if chatPeerId.peerId.namespace == Namespaces.Peer.SecretChat { updatedSecretChatTypingActivities.insert(chatPeerId.peerId) } + case let .AddPeerLiveTypingDraftUpdate(peerAndThreadId, id, timestamp, authorId, text, entities): + liveTypingDraftUpdates[peerAndThreadId] = .update(LiveTypingDraftUpdate.Update( + id: id, + threadId: peerAndThreadId.threadId, + authorId: authorId, + timestamp: timestamp, + text: text, + entities: entities + )) + if peerAndThreadId.threadId != nil { + liveTypingDraftUpdates[PeerAndThreadId(peerId: peerAndThreadId.peerId, threadId: nil)] = .update(LiveTypingDraftUpdate.Update( + id: id, + threadId: peerAndThreadId.threadId, + authorId: authorId, + timestamp: timestamp, + text: text, + entities: entities + )) + } case let .UpdatePinnedItemIds(groupId, pinnedOperation): switch pinnedOperation { case let .pin(itemId): @@ -4733,7 +4806,7 @@ func replayFinalState( break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) case let .UpdateMessageForwardsCount(id, count): transaction.updateMessage(id, update: { currentMessage in @@ -4748,7 +4821,7 @@ func replayFinalState( break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) case let .UpdateInstalledStickerPacks(operation): stickerPackOperations.append(operation) @@ -4932,7 +5005,7 @@ func replayFinalState( tags.remove(.unseenReaction) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) case .UpdateAttachMenuBots: syncAttachMenuBots = true @@ -4957,6 +5030,7 @@ func replayFinalState( return .update(StoreMessage( id: currentMessage.id, + customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, @@ -5000,7 +5074,7 @@ func replayFinalState( } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) }) case let .ResetForumTopic(topicId, data, pts): if finalState.state.resetForumTopicLists[topicId.peerId] == nil { @@ -5691,6 +5765,30 @@ func replayFinalState( _internal_setStarsReactionDefaultPrivacy(privacy: updatedStarsReactionsDefaultPrivacy, transaction: transaction) } + if !liveTypingDraftUpdates.isEmpty { + transaction.combineTypingDrafts(locations: Set(liveTypingDraftUpdates.keys), update: { key, current in + guard let update = liveTypingDraftUpdates[key] else { + return current + } + switch update { + case let .update(update): + return ( + update.id, + update.threadId, + update.authorId, + update.timestamp, + update.text, + [ + TypingDraftMessageAttribute(), + TextEntitiesMessageAttribute(entities: update.entities) + ] + ) + case .cancel: + return nil + } + }) + } + return AccountReplayedFinalState( state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index e620c3e0b4..027f6e3338 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -123,6 +123,7 @@ final class AccountTaskManager { tasks.add(_internal_loadedStickerPack(postbox: self.stateManager.postbox, network: self.stateManager.network, reference: .iconTopicEmoji, forceActualized: true).start()) tasks.add(managedPeerColorUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedStarGiftsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) + tasks.add(managedSavedMusicIdsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) self.managedTopReactionsDisposable.set(managedTopReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 33f68104c1..6989c18fe8 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -450,7 +450,7 @@ public final class AccountViewTracker { break } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) }) } } @@ -798,7 +798,7 @@ public final class AccountViewTracker { if !foundReplies, let repliesCount = repliesCount { attributes.append(ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId)) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -898,7 +898,7 @@ public final class AccountViewTracker { } else { tags.remove(.unseenReaction) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) default: break @@ -1537,7 +1537,7 @@ public final class AccountViewTracker { let peerId = slice[i].0 let value = result[i] transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in - var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, themeEmoticon: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil, greetingMessage: nil, awayMessage: nil, connectedBot: nil, businessIntro: .unknown, birthday: nil, personalChannel: .unknown, botPreview: nil, starGiftsCount: nil, starRefProgram: nil, verification: nil, sendPaidMessageStars: nil, disallowedGifts: [], botGroupAdminRights: nil, botChannelAdminRights: nil, starRating: nil, pendingStarRating: nil) + var cachedData = cachedData as? CachedUserData ?? CachedUserData(about: nil, botInfo: nil, editableBotInfo: nil, peerStatusSettings: nil, pinnedMessageId: nil, isBlocked: false, commonGroupCount: 0, voiceCallsAvailable: true, videoCallsAvailable: true, callsPrivate: true, canPinMessages: true, hasScheduledMessages: true, autoremoveTimeout: .unknown, chatTheme: nil, photo: .unknown, personalPhoto: .unknown, fallbackPhoto: .unknown, voiceMessagesAvailable: true, wallpaper: nil, flags: [], businessHours: nil, businessLocation: nil, greetingMessage: nil, awayMessage: nil, connectedBot: nil, businessIntro: .unknown, birthday: nil, personalChannel: .unknown, botPreview: nil, starGiftsCount: nil, starRefProgram: nil, verification: nil, sendPaidMessageStars: nil, disallowedGifts: [], botGroupAdminRights: nil, botChannelAdminRights: nil, starRating: nil, pendingStarRating: nil, linkedBotChannelId: .unknown, mainProfileTab: nil, savedMusic: nil) var flags = cachedData.flags var sendPaidMessageStars = cachedData.sendPaidMessageStars switch value { @@ -1595,7 +1595,7 @@ public final class AccountViewTracker { } var tags = currentMessage.tags tags.remove(.unseenPersonalMessage) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } @@ -1645,7 +1645,7 @@ public final class AccountViewTracker { break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) transaction.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction()) @@ -1678,7 +1678,7 @@ public final class AccountViewTracker { } var tags = currentMessage.tags tags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } @@ -1719,7 +1719,7 @@ public final class AccountViewTracker { } var tags = currentMessage.tags tags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if transaction.getPendingMessageAction(type: .readReaction, id: id) == nil { diff --git a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift index 8f8303124e..41dd3b684c 100644 --- a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift @@ -316,7 +316,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } } - let updatedMessageValue = StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media) + let updatedMessageValue = StoreMessage(id: updatedId, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media) updatedMessage = updatedMessageValue return .update(updatedMessageValue) @@ -546,7 +546,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities, isPinned: currentMessage.tags.contains(.pinned)) - return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) + return .update(StoreMessage(id: updatedId, customStableId: nil, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) }) } diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index bbad925e9c..5e5e3d0255 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -883,7 +883,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran let updatedFlags = StoreMessageFlags(currentMessage.flags) - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) } }) @@ -923,7 +923,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran default: break } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -956,7 +956,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran default: break } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } else { _internal_deleteMessages(transaction: transaction, mediaBox: postbox.mediaBox, ids: [id]) @@ -991,7 +991,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran default: break } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -1124,7 +1124,7 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa let updatedFlags = StoreMessageFlags(currentMessage.flags) - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) } }) diff --git a/submodules/TelegramCore/Sources/State/ManagedAutoremoveMessageOperations.swift b/submodules/TelegramCore/Sources/State/ManagedAutoremoveMessageOperations.swift index b480b40be6..f40bad06a6 100644 --- a/submodules/TelegramCore/Sources/State/ManagedAutoremoveMessageOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedAutoremoveMessageOperations.swift @@ -111,7 +111,7 @@ func managedAutoremoveMessageOperations(network: Network, postbox: Postbox, isRe break } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) }) } } else { diff --git a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift index 6e0cd45b76..6957dac00f 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift @@ -276,7 +276,7 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox } var updatedTags = currentMessage.tags updatedTags.remove(.unseenPersonalMessage) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -302,7 +302,7 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox } var updatedTags = currentMessage.tags updatedTags.remove(.unseenPersonalMessage) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -344,7 +344,7 @@ private func synchronizeReadMessageReactions(transaction: Transaction, postbox: } var updatedTags = currentMessage.tags updatedTags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -371,7 +371,7 @@ private func synchronizeReadMessageReactions(transaction: Transaction, postbox: } var updatedTags = currentMessage.tags updatedTags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } diff --git a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift index c8e63e28ad..f3400dbef0 100644 --- a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift +++ b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift @@ -343,6 +343,7 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne if let file = files[fileId], let patternFile = files[patternFileId], let numberString = slugComponents.last, let number = Int32(numberString) { let gift = StarGift.UniqueGift( id: id, + giftId: 0, title: title, number: number, slug: slug, @@ -356,7 +357,11 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne giftAddress: nil, resellAmounts: nil, resellForTonOnly: false, - releasedBy: nil + releasedBy: nil, + valueAmount: nil, + valueCurrency: nil, + flags: [], + themePeerId: nil ) if let entry = CodableEntry(RecentStarGiftItem(gift)) { items.append(OrderedItemListEntry(id: RecentStarGiftItemId(id).rawValue, contents: entry)) diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index 93d15dc964..45d409b3bd 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -1822,7 +1822,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia)) }) maybeReadSecretOutgoingMessage(transaction: transaction, index: MessageIndex(id: message.id, timestamp: timestamp)) @@ -1982,6 +1982,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po let storedMessage = StoreMessage( peerId: peerId, namespace: Namespaces.Message.Local, + customStableId: nil, globallyUniqueId: globallyUniqueId, groupingKey: nil, threadId: nil, @@ -2062,7 +2063,7 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) if let resultTimestamp = resultTimestamp { diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift index 542db53ffe..436be75bf5 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift @@ -223,7 +223,7 @@ func markUnseenPersonalMessage(transaction: Transaction, id: MessageId, addSynch break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if addSynchronizeAction { @@ -348,7 +348,7 @@ func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynch } var tags = currentMessage.tags tags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if addSynchronizeAction { diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 7f29516c31..aee701796d 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -165,7 +165,7 @@ public func updateMessageReactionsInteractively(account: Account, messageIds: [M attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge, storeAsRecentlyUsed: storeAsRecentlyUsed, isTags: currentMessage.areReactionsTags(accountPeerId: account.peerId))) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -217,7 +217,7 @@ func _internal_sendStarsReactionsInteractively(account: Account, messageId: Mess resolvedPrivacyValue = resolvedPrivacy - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) return resolvedPrivacyValue @@ -240,7 +240,7 @@ func cancelPendingSendStarsReactionInteractively(account: Account, messageId: Me } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues @@ -281,7 +281,7 @@ func _internal_updateStarsReactionPrivacy(account: Account, messageId: MessageId attributes[j] = ReactionsMessageAttribute(canViewList: attribute.canViewList, isTags: attribute.isTags, reactions: attribute.reactions, recentPeers: attribute.recentPeers, topPeers: updatedTopPeers) } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) var privacyPeerId: PeerId? @@ -392,7 +392,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st if let reactions = reactions { attributes.append(reactions) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) stateManager.addUpdates(result) } @@ -483,7 +483,7 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM if let reactions { attributes.append(reactions) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) stateManager.addUpdates(result) } @@ -706,7 +706,7 @@ private func synchronizeMessageReactions(transaction: Transaction, postbox: Post break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues @@ -730,7 +730,7 @@ private func synchronizeMessageStarsReactions(transaction: Transaction, postbox: break loop } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues diff --git a/submodules/TelegramCore/Sources/State/PeerInputActivity.swift b/submodules/TelegramCore/Sources/State/PeerInputActivity.swift index 267fa01d44..7ad02e13fe 100644 --- a/submodules/TelegramCore/Sources/State/PeerInputActivity.swift +++ b/submodules/TelegramCore/Sources/State/PeerInputActivity.swift @@ -144,6 +144,8 @@ extension PeerInputActivity { } case let .sendMessageEmojiInteractionSeen(emoticon): self = .seeingEmojiInteraction(emoticon: emoticon) + /*case .sendMessageTextDraftAction: + return nil*/ } } } diff --git a/submodules/TelegramCore/Sources/State/PeerInputActivityManager.swift b/submodules/TelegramCore/Sources/State/PeerInputActivityManager.swift index 06ddddb1fc..bcf3472820 100644 --- a/submodules/TelegramCore/Sources/State/PeerInputActivityManager.swift +++ b/submodules/TelegramCore/Sources/State/PeerInputActivityManager.swift @@ -200,126 +200,124 @@ private final class PeerGlobalInputActivityContext { } final class PeerInputActivityManager { - private let queue = Queue() - - private var nextEpisodeId: Int32 = 0 - private var nextUpdateId: Int32 = 0 - private var contexts: [PeerActivitySpace: PeerInputActivityContext] = [:] - private var globalContext: PeerGlobalInputActivityContext? - - func activities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> { - let queue = self.queue - return Signal { [weak self] subscriber in - let disposable = MetaDisposable() - queue.async { - if let strongSelf = self { - let context: PeerInputActivityContext - if let currentContext = strongSelf.contexts[peerId] { - context = currentContext - } else { - context = PeerInputActivityContext(queue: queue, notifyEmpty: { - if let strongSelf = self { - strongSelf.contexts.removeValue(forKey: peerId) - - if let globalContext = strongSelf.globalContext { - let activities = strongSelf.collectActivities() - globalContext.notify(activities) - } - } - }, notifyUpdated: { - if let strongSelf = self, let globalContext = strongSelf.globalContext { - let activities = strongSelf.collectActivities() - globalContext.notify(activities) - } - }) - strongSelf.contexts[peerId] = context - } - let index = context.addSubscriber({ next in - subscriber.putNext(next) - }) - subscriber.putNext(context.topActivities()) - disposable.set(ActionDisposable { - queue.async { - if let strongSelf = self { - if let currentContext = strongSelf.contexts[peerId] { - currentContext.removeSubscriber(index) - if currentContext.isEmpty() { - strongSelf.contexts.removeValue(forKey: peerId) - } - } - } - } - }) - } - } - return disposable - } - } - - private func collectActivities() -> [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] { - assert(self.queue.isCurrent()) + private final class Impl { + let queue: Queue - var dict: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] = [:] - for (chatPeerId, context) in self.contexts { - dict[chatPeerId] = context.topActivities() + var nextEpisodeId: Int32 = 0 + var nextUpdateId: Int32 = 0 + var contexts: [PeerActivitySpace: PeerInputActivityContext] = [:] + var globalContext: PeerGlobalInputActivityContext? + + init(queue: Queue) { + self.queue = queue } - return dict - } - - func allActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError> { - let queue = self.queue - return Signal { [weak self] subscriber in - let disposable = MetaDisposable() - queue.async { - if let strongSelf = self { - let context: PeerGlobalInputActivityContext - if let current = strongSelf.globalContext { - context = current - } else { - context = PeerGlobalInputActivityContext() - strongSelf.globalContext = context + + func activities(peerId: PeerActivitySpace, onNext: @escaping ([(PeerId, PeerInputActivityRecord)]) -> Void) -> Disposable { + let context: PeerInputActivityContext + if let currentContext = self.contexts[peerId] { + context = currentContext + } else { + context = PeerInputActivityContext(queue: queue, notifyEmpty: { [weak self] in + guard let self else { + return } - let index = context.addSubscriber({ next in - subscriber.putNext(next) - }) - subscriber.putNext(strongSelf.collectActivities()) + self.contexts.removeValue(forKey: peerId) - disposable.set(ActionDisposable { - queue.async { - if let strongSelf = self { - if let currentContext = strongSelf.globalContext { - currentContext.removeSubscriber(index) - if currentContext.isEmpty { - strongSelf.globalContext = nil - } - } - } + if let globalContext = self.globalContext { + let activities = self.collectActivities() + globalContext.notify(activities) + } + }, notifyUpdated: { [weak self] in + guard let self else { + return + } + if let globalContext = self.globalContext { + let activities = self.collectActivities() + globalContext.notify(activities) + } + }) + self.contexts[peerId] = context + } + let index = context.addSubscriber({ next in + onNext(next) + }) + onNext(context.topActivities()) + let queue = self.queue + return ActionDisposable { [weak self] in + queue.async { + guard let self else { + return + } + if let currentContext = self.contexts[peerId] { + currentContext.removeSubscriber(index) + if currentContext.isEmpty() { + self.contexts.removeValue(forKey: peerId) } - }) + } } } - return disposable } - } - - func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) { - self.queue.async { + + func allActivities(onNext: @escaping ([PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) -> Void) -> Disposable { + let context: PeerGlobalInputActivityContext + if let current = self.globalContext { + context = current + } else { + context = PeerGlobalInputActivityContext() + self.globalContext = context + } + let index = context.addSubscriber({ next in + onNext(next) + }) + onNext(self.collectActivities()) + + let queue = self.queue + return ActionDisposable { [weak self] in + queue.async { + guard let self else { + return + } + if let currentContext = self.globalContext { + currentContext.removeSubscriber(index) + if currentContext.isEmpty { + self.globalContext = nil + } + } + } + } + } + + private func collectActivities() -> [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] { + assert(self.queue.isCurrent()) + + var dict: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] = [:] + for (chatPeerId, context) in self.contexts { + dict[chatPeerId] = context.topActivities() + } + return dict + } + + func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32?) { let context: PeerInputActivityContext if let currentContext = self.contexts[chatPeerId] { context = currentContext } else { context = PeerInputActivityContext(queue: self.queue, notifyEmpty: { [weak self] in - if let strongSelf = self { - strongSelf.contexts.removeValue(forKey: chatPeerId) - - if let globalContext = strongSelf.globalContext { - let activities = strongSelf.collectActivities() - globalContext.notify(activities) - } + guard let self else { + return + } + self.contexts.removeValue(forKey: chatPeerId) + + if let globalContext = self.globalContext { + let activities = self.collectActivities() + globalContext.notify(activities) } }, notifyUpdated: { [weak self] in - if let strongSelf = self, let globalContext = strongSelf.globalContext { - let activities = strongSelf.collectActivities() + guard let self else { + return + } + if let globalContext = self.globalContext { + let activities = self.collectActivities() globalContext.notify(activities) } }) @@ -347,10 +345,8 @@ final class PeerInputActivityManager { globalContext.notify(activities) } } - } - - func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) { - self.queue.async { + + func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32?) { if let context = self.contexts[chatPeerId] { context.removeActivity(peerId: peerId, activity: activity, episodeId: episodeId) @@ -360,10 +356,8 @@ final class PeerInputActivityManager { } } } - } - - func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) { - self.queue.async { + + func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) { if let currentContext = self.contexts[chatPeerId] { currentContext.removeAllActivities(peerId: peerId) @@ -373,23 +367,17 @@ final class PeerInputActivityManager { } } } - } - - func transaction(_ f: @escaping (PeerInputActivityManager) -> Void) { - self.queue.async { - f(self) - } - } - - func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable { - let disposable = MetaDisposable() - let queue = self.queue - queue.async { + + func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable { + let queue = self.queue let episodeId = self.nextEpisodeId self.nextEpisodeId += 1 let update: () -> Void = { [weak self] in - self?.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) + guard let self else { + return + } + self.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) } let timeout: Double @@ -406,16 +394,69 @@ final class PeerInputActivityManager { timer.start() update() - disposable.set(ActionDisposable { [weak self] in + return ActionDisposable { [weak self] in queue.async { timer.invalidate() - guard let strongSelf = self else { + + guard let self else { return } - - strongSelf.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) + self.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) } - }) + } + } + } + + private let queue = Queue() + private let impl: QueueLocalObject + + init() { + let queue = self.queue + self.impl = QueueLocalObject(queue: queue, generate: { + return Impl(queue: queue) + }) + } + + func activities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> { + return self.impl.signalWith { impl, subscriber in + return impl.activities(peerId: peerId, onNext: subscriber.putNext) + } + } + + func allActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError> { + return self.impl.signalWith { impl, subscriber in + return impl.allActivities(onNext: subscriber.putNext) + } + } + + func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) { + self.impl.with { impl in + impl.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) + } + } + + func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) { + self.impl.with { impl in + impl.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId) + } + } + + func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) { + self.impl.with { impl in + impl.removeAllActivities(chatPeerId: chatPeerId, peerId: peerId) + } + } + + func transaction(_ f: @escaping (PeerInputActivityManager) -> Void) { + self.queue.async { + f(self) + } + } + + func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable { + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.acquireActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity)) } return disposable } diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index 81fc98af0a..b97190d5bd 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -32,21 +32,24 @@ private enum PendingMessageState { case uploading(groupId: Int64?) case waitingToBeSent(groupId: Int64?, content: PendingMessageUploadedContentAndReuploadInfo) case sending(groupId: Int64?) + case waitingForNewTopic(message: Message) var groupId: Int64? { switch self { - case .none: - return nil - case let .collectingInfo(message): - return message.groupingKey - case let .waitingForUploadToStart(groupId, _): - return groupId - case let .uploading(groupId): - return groupId - case let .waitingToBeSent(groupId, _): - return groupId - case let .sending(groupId): - return groupId + case .none: + return nil + case let .collectingInfo(message): + return message.groupingKey + case let .waitingForUploadToStart(groupId, _): + return groupId + case let .uploading(groupId): + return groupId + case let .waitingToBeSent(groupId, _): + return groupId + case let .sending(groupId): + return groupId + case let .waitingForNewTopic(message): + return message.groupingKey } } } @@ -115,6 +118,20 @@ public struct PeerPendingMessageDelivered { private final class PeerPendingMessagesSummaryContext { var messageDeliveredSubscribers = Bag<([PeerPendingMessageDelivered]) -> Void>() var messageFailedSubscribers = Bag<(PendingMessageFailureReason) -> Void>() + var newTopicEvents = Bag<(PendingMessageManager.NewTopicEvent) -> Void>() + + var isEmpty: Bool { + if !self.messageDeliveredSubscribers.isEmpty { + return false + } + if !self.messageFailedSubscribers.isEmpty { + return false + } + if !self.newTopicEvents.isEmpty { + return false + } + return true + } } private enum PendingMessageResult { @@ -158,7 +175,7 @@ private func failMessages(postbox: Postbox, ids: [MessageId]) -> Signal() private let beginSendingMessagesDisposables = DisposableSet() + private var newTopicDisposables: [PeerId: Disposable] = [:] + private var peerSummaryContexts: [PeerId: PeerPendingMessagesSummaryContext] = [:] var transformOutgoingMessageMedia: TransformOutgoingMessageMedia? @@ -226,6 +250,9 @@ public final class PendingMessageManager { deinit { self.beginSendingMessagesDisposables.dispose() + for (_, disposable) in self.newTopicDisposables { + disposable.dispose() + } } func updatePendingMessageIds(_ messageIds: Set) { @@ -422,9 +449,7 @@ public final class PendingMessageManager { assert(strongSelf.queue.isCurrent()) Logger.shared.log("PendingMessageManager", "begin sending, continued: \(ids)") - Logger.shared.log("PendingMessageManager", "beginSendingMessages messages.count: \(messages.count)") - for message in messages.filter({ !$0.flags.contains(.Sending) }).sorted(by: { $0.id < $1.id }) { guard let messageContext = strongSelf.messageContexts[message.id] else { @@ -435,14 +460,110 @@ public final class PendingMessageManager { messageContext.activityType = uploadActivityTypeForMessage(message) } messageContext.threadId = message.threadId - strongSelf.collectUploadingInfo(messageContext: messageContext, message: message) + + if messageContext.threadId == Message.newTopicThreadId { + strongSelf.createNewTopic(messageContext: messageContext, message: message) + } else { + strongSelf.collectUploadingInfo(messageContext: messageContext, message: message) + } } var messagesToUpload: [(PendingMessageContext, Message, PendingMessageUploadedContentType, Signal)] = [] var messagesToForward: [PeerIdAndNamespace: [(PendingMessageContext, Message, ForwardSourceInfoAttribute)]] = [:] Logger.shared.log("PendingMessageManager", "beginSendingMessages messageContexts.count: \(strongSelf.messageContexts.count)") - + + for (messageContext, _) in strongSelf.messageContexts.values.compactMap({ messageContext -> (PendingMessageContext, Message)? in + if case let .waitingForNewTopic(message) = messageContext.state { + return (messageContext, message) + } else { + return nil + } + }).sorted(by: { lhs, rhs in + return lhs.1.index < rhs.1.index + }) { + if case let .waitingForNewTopic(message) = messageContext.state { + let messagePeerId = message.id.peerId + if strongSelf.newTopicDisposables[messagePeerId] == nil { + let disposable = MetaDisposable() + strongSelf.newTopicDisposables[messagePeerId] = disposable + + disposable.set(_internal_createForumChannelTopic( + postbox: strongSelf.postbox, + network: strongSelf.network, + stateManager: strongSelf.stateManager, + accountPeerId: strongSelf.accountPeerId, + peerId: message.id.peerId, + title: "Topic #\(message.stableId)", + iconColor: 0, + iconFileId: nil + ).startStrict(next: { [weak strongSelf] topicId in + guard let strongSelf else { + return + } + var moveMessageIds: [MessageId] = [] + for (_, messageContext) in strongSelf.messageContexts { + if case let .waitingForNewTopic(message) = messageContext.state { + if message.id.peerId == messagePeerId { + moveMessageIds.append(message.id) + } + } + } + if !moveMessageIds.isEmpty { + if let context = strongSelf.peerSummaryContexts[messagePeerId] { + for subscriber in context.newTopicEvents.copyItems() { + subscriber(.willMove(fromThreadId: Message.newTopicThreadId, toThreadId: topicId)) + } + } + + let _ = (strongSelf.postbox.transaction { transaction -> [Message] in + var result: [Message] = [] + for id in moveMessageIds { + transaction.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } + var attributes = currentMessage.attributes + if !attributes.contains(where: { $0 is ReplyMessageAttribute }) { + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: id.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId)), threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) + } + return .update(StoreMessage(id: id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: topicId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + if let message = transaction.getMessage(id) { + result.append(message) + } + } + return result + } + |> deliverOn(strongSelf.queue)).startStandalone(next: { [weak strongSelf] messages in + guard let strongSelf else { + return + } + for message in messages { + if let context = strongSelf.messageContexts[message.id] { + if case .waitingForNewTopic = context.state { + context.state = .collectingInfo(message: message) + } + } + } + strongSelf.newTopicDisposables[messagePeerId]?.dispose() + strongSelf.newTopicDisposables[messagePeerId] = nil + strongSelf.beginSendingMessages(messages.map(\.id)) + + if let context = strongSelf.peerSummaryContexts[messagePeerId] { + for subscriber in context.newTopicEvents.copyItems() { + subscriber(.didMove(fromThreadId: Message.newTopicThreadId, toThreadId: topicId)) + } + } + }) + } + }, error: { _ in + //TODO:release handle errors + })) + } + } + } for (messageContext, _) in strongSelf.messageContexts.values.compactMap({ messageContext -> (PendingMessageContext, Message)? in if case let .collectingInfo(message) = messageContext.state { @@ -486,7 +607,6 @@ public final class PendingMessageManager { } Logger.shared.log("PendingMessageManager", "beginSendingMessages messagesToUpload.count: \(messagesToUpload.count)") - for (messageContext, message, type, contentUploadSignal) in messagesToUpload { if let paidStarsAttribute = message.paidStarsAttribute, paidStarsAttribute.postponeSending { @@ -596,28 +716,32 @@ public final class PendingMessageManager { loop: for (id, context) in self.messageContexts { switch context.state { - case .none: - continue loop - case let .collectingInfo(message): - if message.groupingKey == groupId { - return nil - } - case let .waitingForUploadToStart(contextGroupId, _): - if contextGroupId == groupId { - return nil - } - case let .uploading(contextGroupId): - if contextGroupId == groupId { - return nil - } - case let .waitingToBeSent(contextGroupId, content): - if contextGroupId == groupId { - result.append((context, id, content)) - } - case let .sending(contextGroupId): - if contextGroupId == groupId { - return nil - } + case .none: + continue loop + case let .collectingInfo(message): + if message.groupingKey == groupId { + return nil + } + case let .waitingForUploadToStart(contextGroupId, _): + if contextGroupId == groupId { + return nil + } + case let .uploading(contextGroupId): + if contextGroupId == groupId { + return nil + } + case let .waitingToBeSent(contextGroupId, content): + if contextGroupId == groupId { + result.append((context, id, content)) + } + case let .sending(contextGroupId): + if contextGroupId == groupId { + return nil + } + case let .waitingForNewTopic(message): + if message.groupingKey == groupId { + return nil + } } } @@ -665,6 +789,10 @@ public final class PendingMessageManager { })) } + private func createNewTopic(messageContext: PendingMessageContext, message: Message) { + messageContext.state = .waitingForNewTopic(message: message) + } + private func collectUploadingInfo(messageContext: PendingMessageContext, message: Message) { messageContext.state = .collectingInfo(message: message) } @@ -724,7 +852,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } return modify @@ -1307,7 +1435,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } else { let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: message.id.peerId, operation: .sendMessage(layer: layer, id: message.id, file: secretFile), state: state) @@ -1323,7 +1451,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } else { @@ -1332,7 +1460,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } @@ -1850,7 +1978,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) }).start() } @@ -1864,7 +1992,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } @@ -1892,7 +2020,7 @@ public final class PendingMessageManager { 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } @@ -2030,7 +2158,7 @@ public final class PendingMessageManager { self.queue.async { if let current = self.peerSummaryContexts[peerId] { current.messageDeliveredSubscribers.remove(index) - if current.messageDeliveredSubscribers.isEmpty { + if current.isEmpty { self.peerSummaryContexts.removeValue(forKey: peerId) } } @@ -2063,7 +2191,40 @@ public final class PendingMessageManager { self.queue.async { if let current = self.peerSummaryContexts[peerId] { current.messageFailedSubscribers.remove(index) - if current.messageFailedSubscribers.isEmpty { + if current.isEmpty { + self.peerSummaryContexts.removeValue(forKey: peerId) + } + } + } + }) + } + + return disposable + } + } + + public func newTopicEvents(peerId: PeerId) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + + self.queue.async { + let summaryContext: PeerPendingMessagesSummaryContext + if let current = self.peerSummaryContexts[peerId] { + summaryContext = current + } else { + summaryContext = PeerPendingMessagesSummaryContext() + self.peerSummaryContexts[peerId] = summaryContext + } + + let index = summaryContext.newTopicEvents.add({ reason in + subscriber.putNext(reason) + }) + + disposable.set(ActionDisposable { + self.queue.async { + if let current = self.peerSummaryContexts[peerId] { + current.newTopicEvents.remove(index) + if current.isEmpty { self.peerSummaryContexts.removeValue(forKey: peerId) } } diff --git a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift index a0a260b165..753cb0a3e6 100644 --- a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift @@ -567,7 +567,7 @@ extension StoreMessage { convenience init?(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi8.DecryptedMessage, file: SecretChatFileReference?) { switch apiMessage { case let .decryptedMessage(randomId, _, message, _): - self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: message, attributes: [], media: []) + self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: message, attributes: [], media: []) case let .decryptedMessageService(randomId, _, action): switch action { case .decryptedMessageActionDeleteMessages: @@ -579,9 +579,9 @@ extension StoreMessage { case .decryptedMessageActionReadMessages: return nil case .decryptedMessageActionScreenshotMessages: - self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]) + self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): - self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]) + self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]) } } } @@ -894,7 +894,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): switch action { case .decryptedMessageActionDeleteMessages: @@ -906,9 +906,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 case .decryptedMessageActionReadMessages: return nil case .decryptedMessageActionScreenshotMessages: - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) case .decryptedMessageActionResend: return nil case .decryptedMessageActionRequestKey: @@ -1126,7 +1126,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): switch action { case .decryptedMessageActionDeleteMessages: @@ -1138,9 +1138,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 case .decryptedMessageActionReadMessages: return nil case .decryptedMessageActionScreenshotMessages: - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) case .decryptedMessageActionResend: return nil case .decryptedMessageActionRequestKey: @@ -1405,7 +1405,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): switch action { case .decryptedMessageActionDeleteMessages: @@ -1417,9 +1417,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 case .decryptedMessageActionReadMessages: return nil case .decryptedMessageActionScreenshotMessages: - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) case .decryptedMessageActionResend: return nil case .decryptedMessageActionRequestKey: @@ -1606,7 +1606,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): switch action { case .decryptedMessageActionDeleteMessages: @@ -1618,9 +1618,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 case .decryptedMessageActionReadMessages: return nil case .decryptedMessageActionScreenshotMessages: - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) case let .decryptedMessageActionSetMessageTTL(ttlSeconds): - return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), []) case .decryptedMessageActionResend: return nil case .decryptedMessageActionRequestKey: diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 1cda543eba..1de180df61 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 211 + return 214 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift index 1212662ce2..e04ff49eb1 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift @@ -259,7 +259,7 @@ public final class CachedChannelData: CachedPeerData { public let activeCall: ActiveCall? public let callJoinPeerId: PeerId? public let pendingSuggestions: [String] - public let themeEmoticon: String? + public let chatTheme: ChatTheme? public let inviteRequestsPending: Int32? public let sendAsPeerId: PeerId? public let reactionSettings: EnginePeerCachedInfoItem @@ -272,6 +272,7 @@ public final class CachedChannelData: CachedPeerData { public let verification: PeerVerification? public let starGiftsCount: Int32? public let sendPaidMessageStars: StarsAmount? + public let mainProfileTab: TelegramProfileTab? public let peerIds: Set public let messageIds: Set @@ -306,7 +307,7 @@ public final class CachedChannelData: CachedPeerData { self.activeCall = nil self.callJoinPeerId = nil self.pendingSuggestions = [] - self.themeEmoticon = nil + self.chatTheme = nil self.inviteRequestsPending = nil self.sendAsPeerId = nil self.reactionSettings = .unknown @@ -319,6 +320,7 @@ public final class CachedChannelData: CachedPeerData { self.verification = nil self.starGiftsCount = nil self.sendPaidMessageStars = nil + self.mainProfileTab = nil } public init( @@ -346,7 +348,7 @@ public final class CachedChannelData: CachedPeerData { callJoinPeerId: PeerId?, autoremoveTimeout: CachedPeerAutoremoveTimeout, pendingSuggestions: [String], - themeEmoticon: String?, + chatTheme: ChatTheme?, inviteRequestsPending: Int32?, sendAsPeerId: PeerId?, reactionSettings: EnginePeerCachedInfoItem, @@ -358,7 +360,8 @@ public final class CachedChannelData: CachedPeerData { emojiPack: StickerPackCollectionInfo?, verification: PeerVerification?, starGiftsCount: Int32?, - sendPaidMessageStars: StarsAmount? + sendPaidMessageStars: StarsAmount?, + mainProfileTab: TelegramProfileTab? ) { self.isNotAccessible = isNotAccessible self.flags = flags @@ -384,7 +387,7 @@ public final class CachedChannelData: CachedPeerData { self.callJoinPeerId = callJoinPeerId self.autoremoveTimeout = autoremoveTimeout self.pendingSuggestions = pendingSuggestions - self.themeEmoticon = themeEmoticon + self.chatTheme = chatTheme self.inviteRequestsPending = inviteRequestsPending self.sendAsPeerId = sendAsPeerId self.reactionSettings = reactionSettings @@ -397,6 +400,7 @@ public final class CachedChannelData: CachedPeerData { self.verification = verification self.starGiftsCount = starGiftsCount self.sendPaidMessageStars = sendPaidMessageStars + self.mainProfileTab = mainProfileTab var peerIds = Set() for botInfo in botInfos { @@ -424,153 +428,157 @@ public final class CachedChannelData: CachedPeerData { } public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData { - return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedAbout(_ about: String?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedInvitedOn(_ invitedOn: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedActiveCall(_ activeCall: ActiveCall?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedPendingSuggestions(_ pendingSuggestions: [String]) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } - public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + public func withUpdatedChatTheme(_ chatTheme: ChatTheme?) -> CachedChannelData { + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedInviteRequestsPending(_ inviteRequestsPending: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedSendAsPeerId(_ sendAsPeerId: PeerId?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedReactionSettings(_ reactionSettings: EnginePeerCachedInfoItem) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedMembersHidden(_ membersHidden: EnginePeerCachedInfoItem) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedViewForumAsMessages(_ viewForumAsMessages: EnginePeerCachedInfoItem) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedWallpaper(_ wallpaper: TelegramWallpaper?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedBoostsToUnrestrict(_ boostsToUnrestrict: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedAppliedBoosts(_ appliedBoosts: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedEmojiPack(_ emojiPack: StickerPackCollectionInfo?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedVerification(_ verification: PeerVerification?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedStarGiftsCount(_ starGiftsCount: Int32?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } public func withUpdatedSendPaidMessageStars(_ sendPaidMessageStars: StarsAmount?) -> CachedChannelData { - return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: sendPaidMessageStars) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: sendPaidMessageStars, mainProfileTab: self.mainProfileTab) } + public func withUpdatedMainProfileTab(_ mainProfileTab: TelegramProfileTab?) -> CachedChannelData { + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, reactionSettings: self.reactionSettings, membersHidden: self.membersHidden, viewForumAsMessages: self.viewForumAsMessages, wallpaper: self.wallpaper, boostsToUnrestrict: self.boostsToUnrestrict, appliedBoosts: self.appliedBoosts, emojiPack: self.emojiPack, verification: self.verification, starGiftsCount: self.starGiftsCount, sendPaidMessageStars: self.sendPaidMessageStars, mainProfileTab: mainProfileTab) + } + public init(decoder: PostboxDecoder) { self.isNotAccessible = decoder.decodeInt32ForKey("isNotAccessible", orElse: 0) != 0 self.flags = CachedChannelFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)) @@ -646,7 +654,13 @@ public final class CachedChannelData: CachedPeerData { self.pendingSuggestions = decoder.decodeStringArrayForKey("sug") - self.themeEmoticon = decoder.decodeOptionalStringForKey("te") + if let chatThemeData = decoder.decodeDataForKey("ct"), let chatTheme = try? AdaptedPostboxDecoder().decode(ChatTheme.self, from: chatThemeData) { + self.chatTheme = chatTheme + } else if let themeEmoticon = decoder.decodeOptionalStringForKey("te") { + self.chatTheme = .emoticon(themeEmoticon) + } else { + self.chatTheme = nil + } if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage { self.photo = photo @@ -705,6 +719,8 @@ public final class CachedChannelData: CachedPeerData { self.sendPaidMessageStars = decoder.decodeCodable(StarsAmount.self, forKey: "sendPaidMessageStars") self.verification = decoder.decodeCodable(PeerVerification.self, forKey: "vf") + + self.mainProfileTab = decoder.decodeCodable(TelegramProfileTab.self, forKey: "mainProfileTab") self.peerIds = peerIds @@ -827,10 +843,10 @@ public final class CachedChannelData: CachedPeerData { encoder.encodeStringArray(self.pendingSuggestions, forKey: "sug") - if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty { - encoder.encodeString(themeEmoticon, forKey: "te") + if let chatTheme = self.chatTheme, let chatThemeData = try? AdaptedPostboxEncoder().encode(chatTheme) { + encoder.encodeData(chatThemeData, forKey: "ct") } else { - encoder.encodeNil(forKey: "te") + encoder.encodeNil(forKey: "ct") } if let inviteRequestsPending = self.inviteRequestsPending { @@ -907,6 +923,12 @@ public final class CachedChannelData: CachedPeerData { } else { encoder.encodeNil(forKey: "sendPaidMessageStars") } + + if let mainProfileTab = self.mainProfileTab { + encoder.encodeCodable(mainProfileTab, forKey: "mainProfileTab") + } else { + encoder.encodeNil(forKey: "mainProfileTab") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -1010,7 +1032,7 @@ public final class CachedChannelData: CachedPeerData { return false } - if other.themeEmoticon != self.themeEmoticon { + if other.chatTheme != self.chatTheme { return false } @@ -1053,6 +1075,9 @@ public final class CachedChannelData: CachedPeerData { if other.verification != self.verification { return false } + if other.mainProfileTab != self.mainProfileTab { + return false + } return true } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift index 544760a6b3..1ee1f64b2e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift @@ -139,7 +139,7 @@ public final class CachedGroupData: CachedPeerData { public let autoremoveTimeout: CachedPeerAutoremoveTimeout public let activeCall: CachedChannelData.ActiveCall? public let callJoinPeerId: PeerId? - public let themeEmoticon: String? + public let chatTheme: ChatTheme? public let inviteRequestsPending: Int32? public let reactionSettings: EnginePeerCachedInfoItem @@ -164,7 +164,7 @@ public final class CachedGroupData: CachedPeerData { self.autoremoveTimeout = .unknown self.activeCall = nil self.callJoinPeerId = nil - self.themeEmoticon = nil + self.chatTheme = nil self.inviteRequestsPending = nil self.reactionSettings = .unknown } @@ -183,7 +183,7 @@ public final class CachedGroupData: CachedPeerData { activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout, callJoinPeerId: PeerId?, - themeEmoticon: String?, + chatTheme: ChatTheme?, inviteRequestsPending: Int32?, reactionSettings: EnginePeerCachedInfoItem ) { @@ -200,7 +200,7 @@ public final class CachedGroupData: CachedPeerData { self.activeCall = activeCall self.autoremoveTimeout = autoremoveTimeout self.callJoinPeerId = callJoinPeerId - self.themeEmoticon = themeEmoticon + self.chatTheme = chatTheme self.inviteRequestsPending = inviteRequestsPending self.reactionSettings = reactionSettings @@ -263,7 +263,13 @@ public final class CachedGroupData: CachedPeerData { self.callJoinPeerId = decoder.decodeOptionalInt64ForKey("callJoinPeerId").flatMap(PeerId.init) - self.themeEmoticon = decoder.decodeOptionalStringForKey("te") + if let chatThemeData = decoder.decodeDataForKey("ct"), let chatTheme = try? AdaptedPostboxDecoder().decode(ChatTheme.self, from: chatThemeData) { + self.chatTheme = chatTheme + } else if let themeEmoticon = decoder.decodeOptionalStringForKey("te") { + self.chatTheme = .emoticon(themeEmoticon) + } else { + self.chatTheme = nil + } self.inviteRequestsPending = decoder.decodeOptionalInt32ForKey("irp") @@ -357,10 +363,10 @@ public final class CachedGroupData: CachedPeerData { encoder.encodeNil(forKey: "callJoinPeerId") } - if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty { - encoder.encodeString(themeEmoticon, forKey: "te") + if let chatTheme = self.chatTheme, let chatThemeData = try? AdaptedPostboxEncoder().encode(chatTheme) { + encoder.encodeData(chatThemeData, forKey: "ct") } else { - encoder.encodeNil(forKey: "te") + encoder.encodeNil(forKey: "ct") } if let inviteRequestsPending = self.inviteRequestsPending { @@ -394,70 +400,70 @@ public final class CachedGroupData: CachedPeerData { return false } - return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy && self.themeEmoticon == other.themeEmoticon && self.inviteRequestsPending == other.inviteRequestsPending + return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy && self.chatTheme == other.chatTheme && self.inviteRequestsPending == other.inviteRequestsPending } public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData { - return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedAbout(_ about: String?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } - public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) + public func withUpdatedChatTheme(_ chatTheme: ChatTheme?) -> CachedGroupData { + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedInviteRequestsPending(_ inviteRequestsPending: Int32?) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: inviteRequestsPending, reactionSettings: self.reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: inviteRequestsPending, reactionSettings: self.reactionSettings) } public func withUpdatedReactionSettings(_ reactionSettings: EnginePeerCachedInfoItem) -> CachedGroupData { - return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: reactionSettings) + return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, chatTheme: self.chatTheme, inviteRequestsPending: self.inviteRequestsPending, reactionSettings: reactionSettings) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index 5ae9a1817a..f4a53c5fdd 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -927,6 +927,61 @@ public final class TelegramStarPendingRating: Codable, Equatable { } } +public enum TelegramProfileTab: Int32, Codable, Hashable { + case files + case gifs + case gifts + case links + case media + case music + case posts + case voice +} + +extension TelegramProfileTab { + init(apiTab: Api.ProfileTab) { + switch apiTab { + case .profileTabFiles: + self = .files + case .profileTabGifs: + self = .gifs + case .profileTabGifts: + self = .gifts + case .profileTabLinks: + self = .links + case .profileTabMedia: + self = .media + case .profileTabMusic: + self = .music + case .profileTabPosts: + self = .posts + case .profileTabVoice: + self = .voice + } + } + + var apiTab: Api.ProfileTab { + switch self { + case .files: + return .profileTabFiles + case .gifs: + return .profileTabGifs + case .gifts: + return .profileTabGifts + case .links: + return .profileTabLinks + case .media: + return .profileTabMedia + case .music: + return .profileTabMusic + case .posts: + return .profileTabPosts + case .voice: + return .profileTabVoice + } + } +} + extension TelegramStarRating { convenience init(apiRating: Api.StarsRating) { switch apiRating { @@ -1031,6 +1086,20 @@ public final class CachedUserData: CachedPeerData { } } + public enum LinkedBotChannelId: Equatable { + case unknown + case known(PeerId?) + + public var value: PeerId? { + switch self { + case .unknown: + return nil + case let .known(peerId): + return peerId + } + } + } + public let about: String? public let botInfo: BotInfo? public let editableBotInfo: EditableBotInfo? @@ -1044,7 +1113,7 @@ public final class CachedUserData: CachedPeerData { public let canPinMessages: Bool public let hasScheduledMessages: Bool public let autoremoveTimeout: CachedPeerAutoremoveTimeout - public let themeEmoticon: String? + public let chatTheme: ChatTheme? public let photo: CachedPeerProfilePhoto public let personalPhoto: CachedPeerProfilePhoto public let fallbackPhoto: CachedPeerProfilePhoto @@ -1069,6 +1138,9 @@ public final class CachedUserData: CachedPeerData { public let botChannelAdminRights: TelegramChatAdminRights? public let starRating: TelegramStarRating? public let pendingStarRating: TelegramStarPendingRating? + public let linkedBotChannelId: LinkedBotChannelId + public let mainProfileTab: TelegramProfileTab? + public let savedMusic: TelegramMediaFile? public let peerIds: Set public let messageIds: Set @@ -1088,7 +1160,7 @@ public final class CachedUserData: CachedPeerData { self.canPinMessages = false self.hasScheduledMessages = false self.autoremoveTimeout = .unknown - self.themeEmoticon = nil + self.chatTheme = nil self.photo = .unknown self.personalPhoto = .unknown self.fallbackPhoto = .unknown @@ -1115,9 +1187,12 @@ public final class CachedUserData: CachedPeerData { self.botChannelAdminRights = nil self.starRating = nil self.pendingStarRating = nil + self.linkedBotChannelId = .unknown + self.mainProfileTab = nil + self.savedMusic = nil } - public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts?, botGroupAdminRights: TelegramChatAdminRights?, botChannelAdminRights: TelegramChatAdminRights?, starRating: TelegramStarRating?, pendingStarRating: TelegramStarPendingRating?) { + public init(about: String?, botInfo: BotInfo?, editableBotInfo: EditableBotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, chatTheme: ChatTheme?, photo: CachedPeerProfilePhoto, personalPhoto: CachedPeerProfilePhoto, fallbackPhoto: CachedPeerProfilePhoto, voiceMessagesAvailable: Bool, wallpaper: TelegramWallpaper?, flags: CachedUserFlags, businessHours: TelegramBusinessHours?, businessLocation: TelegramBusinessLocation?, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, connectedBot: TelegramAccountConnectedBot?, businessIntro: CachedTelegramBusinessIntro, birthday: TelegramBirthday?, personalChannel: CachedTelegramPersonalChannel, botPreview: BotPreview?, starGiftsCount: Int32?, starRefProgram: TelegramStarRefProgram?, verification: PeerVerification?, sendPaidMessageStars: StarsAmount?, disallowedGifts: TelegramDisallowedGifts?, botGroupAdminRights: TelegramChatAdminRights?, botChannelAdminRights: TelegramChatAdminRights?, starRating: TelegramStarRating?, pendingStarRating: TelegramStarPendingRating?, linkedBotChannelId: LinkedBotChannelId, mainProfileTab: TelegramProfileTab?, savedMusic: TelegramMediaFile?) { self.about = about self.botInfo = botInfo self.editableBotInfo = editableBotInfo @@ -1131,7 +1206,7 @@ public final class CachedUserData: CachedPeerData { self.canPinMessages = canPinMessages self.hasScheduledMessages = hasScheduledMessages self.autoremoveTimeout = autoremoveTimeout - self.themeEmoticon = themeEmoticon + self.chatTheme = chatTheme self.photo = photo self.personalPhoto = personalPhoto self.fallbackPhoto = fallbackPhoto @@ -1156,6 +1231,9 @@ public final class CachedUserData: CachedPeerData { self.botChannelAdminRights = botChannelAdminRights self.starRating = starRating self.pendingStarRating = pendingStarRating + self.linkedBotChannelId = linkedBotChannelId + self.mainProfileTab = mainProfileTab + self.savedMusic = savedMusic self.peerIds = Set() @@ -1190,7 +1268,14 @@ public final class CachedUserData: CachedPeerData { self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0 self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false) self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown - self.themeEmoticon = decoder.decodeOptionalStringForKey("te") + + if let chatThemeData = decoder.decodeDataForKey("ct"), let chatTheme = try? AdaptedPostboxDecoder().decode(ChatTheme.self, from: chatThemeData) { + self.chatTheme = chatTheme + } else if let chatTheme = decoder.decodeOptionalStringForKey("te") { + self.chatTheme = .emoticon(chatTheme) + } else { + self.chatTheme = nil + } self.photo = decoder.decodeObjectForKey("phv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown self.personalPhoto = decoder.decodeObjectForKey("pphv", decoder: CachedPeerProfilePhoto.init(decoder:)) as? CachedPeerProfilePhoto ?? .unknown @@ -1236,6 +1321,24 @@ public final class CachedUserData: CachedPeerData { self.starRating = decoder.decodeCodable(TelegramStarRating.self, forKey: "starRating") self.pendingStarRating = decoder.decodeCodable(TelegramStarPendingRating.self, forKey: "pendingStarRating") + + self.mainProfileTab = decoder.decodeCodable(TelegramProfileTab.self, forKey: "mainProfileTab") + + if let savedMusic = decoder.decodeObjectForKey("savedMusic", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile { + self.savedMusic = savedMusic + } else { + self.savedMusic = nil + } + + if let linkedBotChannelId = decoder.decodeOptionalInt64ForKey("linkedBotChannelId") { + if linkedBotChannelId == 0 { + self.linkedBotChannelId = .known(nil) + } else { + self.linkedBotChannelId = .known(PeerId(linkedBotChannelId)) + } + } else { + self.linkedBotChannelId = .unknown + } } public func encode(_ encoder: PostboxEncoder) { @@ -1276,10 +1379,11 @@ public final class CachedUserData: CachedPeerData { encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm") encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm") encoder.encodeObject(self.autoremoveTimeout, forKey: "artv") - if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty { - encoder.encodeString(themeEmoticon, forKey: "te") + + if let chatTheme = self.chatTheme, let chatThemeData = try? AdaptedPostboxEncoder().encode(chatTheme) { + encoder.encodeData(chatThemeData, forKey: "ct") } else { - encoder.encodeNil(forKey: "te") + encoder.encodeNil(forKey: "ct") } encoder.encodeObject(self.photo, forKey: "phv") @@ -1394,6 +1498,23 @@ public final class CachedUserData: CachedPeerData { } else { encoder.encodeNil(forKey: "pendingStarRating") } + + switch self.linkedBotChannelId { + case .unknown: + encoder.encodeNil(forKey: "linkedBotChannelId") + case let .known(value): + if let value = value { + encoder.encodeInt64(value.toInt64(), forKey: "linkedBotChannelId") + } else { + encoder.encodeInt64(0, forKey: "linkedBotChannelId") + } + } + + if let mainProfileTab = self.mainProfileTab { + encoder.encodeCodable(mainProfileTab, forKey: "mainProfileTab") + } else { + encoder.encodeNil(forKey: "mainProfileTab") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -1461,160 +1582,181 @@ public final class CachedUserData: CachedPeerData { if other.pendingStarRating != self.pendingStarRating { return false } + if other.linkedBotChannelId != self.linkedBotChannelId { + return false + } + if other.mainProfileTab != self.mainProfileTab { + return false + } + if other.savedMusic != self.savedMusic { + return false + } - return other.about == self.about && other.botInfo == self.botInfo && other.editableBotInfo == self.editableBotInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.themeEmoticon == other.themeEmoticon && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.fallbackPhoto == other.fallbackPhoto && self.voiceMessagesAvailable == other.voiceMessagesAvailable && self.flags == other.flags && self.wallpaper == other.wallpaper + return other.about == self.about && other.botInfo == self.botInfo && other.editableBotInfo == self.editableBotInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.chatTheme == other.chatTheme && self.photo == other.photo && self.personalPhoto == other.personalPhoto && self.fallbackPhoto == other.fallbackPhoto && self.voiceMessagesAvailable == other.voiceMessagesAvailable && self.flags == other.flags && self.wallpaper == other.wallpaper } public func withUpdatedAbout(_ about: String?) -> CachedUserData { - return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedEditableBotInfo(_ editableBotInfo: EditableBotInfo?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } - public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + public func withUpdatedChatTheme(_ chatTheme: ChatTheme?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPhoto(_ photo: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPersonalPhoto(_ personalPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedFallbackPhoto(_ fallbackPhoto: CachedPeerProfilePhoto) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedWallpaper(_ wallpaper: TelegramWallpaper?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedFlags(_ flags: CachedUserFlags) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBusinessHours(_ businessHours: TelegramBusinessHours?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBusinessLocation(_ businessLocation: TelegramBusinessLocation?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedGreetingMessage(_ greetingMessage: TelegramBusinessGreetingMessage?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedAwayMessage(_ awayMessage: TelegramBusinessAwayMessage?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedConnectedBot(_ connectedBot: TelegramAccountConnectedBot?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBusinessIntro(_ businessIntro: TelegramBusinessIntro?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: .known(businessIntro), birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: .known(businessIntro), birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBirthday(_ birthday: TelegramBirthday?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPersonalChannel(_ personalChannel: TelegramPersonalChannel?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: .known(personalChannel), botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: .known(personalChannel), botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBotPreview(_ botPreview: BotPreview?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedStarGiftsCount(_ starGiftsCount: Int32?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedStarRefProgram(_ starRefProgram: TelegramStarRefProgram?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedVerification(_ verification: PeerVerification?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedSendPaidMessageStars(_ sendPaidMessageStars: StarsAmount?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: sendPaidMessageStars, disallowedGifts: self.disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedDisallowedGifts(_ disallowedGifts: TelegramDisallowedGifts) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBotGroupAdminRights(_ botGroupAdminRights: TelegramChatAdminRights?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedBotChannelAdminRights(_ botChannelAdminRights: TelegramChatAdminRights?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedStarRating(_ starRating: TelegramStarRating?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: starRating, pendingStarRating: self.pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) } public func withUpdatedPendingStarRating(_ pendingStarRating: TelegramStarPendingRating?) -> CachedUserData { - return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: pendingStarRating) + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) + } + + public func withUpdatedLinkedBotChannelId(_ linkedBotChannelId: PeerId?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: .known(linkedBotChannelId), mainProfileTab: self.mainProfileTab, savedMusic: self.savedMusic) + } + + public func withUpdatedMainProfileTab(_ mainProfileTab: TelegramProfileTab?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: mainProfileTab, savedMusic: self.savedMusic) + } + + public func withUpdatedSavedMusic(_ savedMusic: TelegramMediaFile?) -> CachedUserData { + return CachedUserData(about: self.about, botInfo: self.botInfo, editableBotInfo: self.editableBotInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, chatTheme: self.chatTheme, photo: self.photo, personalPhoto: self.personalPhoto, fallbackPhoto: self.fallbackPhoto, voiceMessagesAvailable: self.voiceMessagesAvailable, wallpaper: self.wallpaper, flags: self.flags, businessHours: self.businessHours, businessLocation: self.businessLocation, greetingMessage: self.greetingMessage, awayMessage: self.awayMessage, connectedBot: self.connectedBot, businessIntro: self.businessIntro, birthday: self.birthday, personalChannel: self.personalChannel, botPreview: self.botPreview, starGiftsCount: self.starGiftsCount, starRefProgram: self.starRefProgram, verification: self.verification, sendPaidMessageStars: self.sendPaidMessageStars, disallowedGifts: disallowedGifts, botGroupAdminRights: self.botGroupAdminRights, botChannelAdminRights: self.botChannelAdminRights, starRating: self.starRating, pendingStarRating: self.pendingStarRating, linkedBotChannelId: self.linkedBotChannelId, mainProfileTab: self.mainProfileTab, savedMusic: savedMusic) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift index d0f1e0a6bc..d5be2f2137 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift @@ -277,6 +277,7 @@ public enum AnyMediaReference: Equatable { case customEmoji(media: Media) case story(peer: PeerReference, id: Int32, media: Media) case starsTransaction(transaction: StarsTransactionReference, media: Media) + case savedMusic(peer: PeerReference, media: Media) public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool { switch lhs { @@ -352,6 +353,12 @@ public enum AnyMediaReference: Equatable { } else { return false } + case let .savedMusic(lhsPeer, lhsMedia): + if case let .savedMusic(rhsPeer, rhsMedia) = rhs, lhsPeer == rhsPeer, lhsMedia.isEqual(to: rhsMedia) { + return true + } else { + return false + } } } @@ -381,6 +388,8 @@ public enum AnyMediaReference: Equatable { return nil case .starsTransaction: return nil + case let .savedMusic(peer, _): + return .savedMusic(peer: peer) } } @@ -434,6 +443,10 @@ public enum AnyMediaReference: Equatable { if let media = media as? T { return .starsTransaction(transaction: transaction, media: media) } + case let .savedMusic(peer, media): + if let media = media as? T { + return .savedMusic(peer: peer, media: media) + } } return nil } @@ -464,6 +477,8 @@ public enum AnyMediaReference: Equatable { return media case let .starsTransaction(_, media): return media + case let .savedMusic(_, media): + return media } } @@ -493,6 +508,8 @@ public enum AnyMediaReference: Equatable { return .story(peer: peer, id: id, media: media) case let .starsTransaction(transaction, _): return .starsTransaction(transaction: transaction, media: media) + case let .savedMusic(peer, _): + return .savedMusic(peer: peer, media: media) } } @@ -509,6 +526,7 @@ public enum PartialMediaReference: Equatable { case savedGif case savedSticker case recentSticker + case savedMusic } case message(message: MessageReference) @@ -517,6 +535,7 @@ public enum PartialMediaReference: Equatable { case savedGif case savedSticker case recentSticker + case savedMusic(peer: PeerReference) public init?(decoder: PostboxDecoder) { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { @@ -538,6 +557,9 @@ public enum PartialMediaReference: Equatable { self = .savedSticker case .recentSticker: self = .recentSticker + case .savedMusic: + let peer = decoder.decodeObjectForKey("pg", decoder: { PeerReference(decoder: $0) }) as! PeerReference + self = .savedMusic(peer: peer) } } @@ -558,6 +580,9 @@ public enum PartialMediaReference: Equatable { encoder.encodeInt32(CodingCase.savedSticker.rawValue, forKey: "_r") case .recentSticker: encoder.encodeInt32(CodingCase.recentSticker.rawValue, forKey: "_r") + case let .savedMusic(peer): + encoder.encodeInt32(CodingCase.savedMusic.rawValue, forKey: "_r") + encoder.encodeObject(peer, forKey: "pg") } } @@ -575,6 +600,8 @@ public enum PartialMediaReference: Equatable { return .savedSticker(media: media) case .recentSticker: return .recentSticker(media: media) + case let .savedMusic(peer): + return .savedMusic(peer: peer, media: media) } } } @@ -593,6 +620,7 @@ public enum MediaReference { case customEmoji case story case starsTransaction + case savedMusic } case standalone(media: T) @@ -607,6 +635,7 @@ public enum MediaReference { case customEmoji(media: T) case story(peer: PeerReference, id: Int32, media: T) case starsTransaction(transaction: StarsTransactionReference, media: T) + case savedMusic(peer: PeerReference, media: T) public init?(decoder: PostboxDecoder) { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { @@ -681,6 +710,12 @@ public enum MediaReference { return nil } self = .starsTransaction(transaction: transaction, media: media) + case .savedMusic: + let peer = decoder.decodeObjectForKey("pr", decoder: { PeerReference(decoder: $0) }) as! PeerReference + guard let media = decoder.decodeObjectForKey("m") as? T else { + return nil + } + self = .savedMusic(peer: peer, media: media) } } @@ -730,9 +765,12 @@ public enum MediaReference { encoder.encodeInt32(CodingCase.starsTransaction.rawValue, forKey: "_r") encoder.encodeObject(transaction, forKey: "tr") encoder.encodeObject(media, forKey: "m") + case let .savedMusic(peer, media): + encoder.encodeInt32(CodingCase.savedMusic.rawValue, forKey: "_r") + encoder.encodeObject(peer, forKey: "pr") + encoder.encodeObject(media, forKey: "m") } } - public var abstract: AnyMediaReference { switch self { @@ -760,6 +798,8 @@ public enum MediaReference { return .story(peer: peer, id: id, media: media) case let .starsTransaction(transaction, media): return .starsTransaction(transaction: transaction, media: media) + case let .savedMusic(peer, media): + return .savedMusic(peer: peer, media: media) } } @@ -793,6 +833,8 @@ public enum MediaReference { return media case let .starsTransaction(_, media): return media + case let .savedMusic(_, media): + return media } } @@ -822,6 +864,8 @@ public enum MediaReference { return .story(peer: peer, id: id, media: media) case let .starsTransaction(transaction, _): return .starsTransaction(transaction: transaction, media: media) + case let .savedMusic(peer, _): + return .savedMusic(peer: peer, media: media) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReferenceFlatBuffers.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReferenceFlatBuffers.swift index c083134b6e..881a0be6e0 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReferenceFlatBuffers.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReferenceFlatBuffers.swift @@ -106,6 +106,15 @@ public extension PartialMediaReference { self = .savedSticker case .partialmediareferenceRecentsticker: self = .recentSticker + case .partialmediareferenceSavedmusic: + guard let value = flatBuffersObject.value(type: TelegramCore_PartialMediaReference_SavedMusic.self) else { + throw FlatBuffersError.missingRequiredField() + } + if let peer = value.peer { + self = .savedMusic(peer: try PeerReference(flatBuffersObject: peer)) + } else { + return nil + } case .none_: throw FlatBuffersError.missingRequiredField() } @@ -149,6 +158,12 @@ public extension PartialMediaReference { let start = TelegramCore_PartialMediaReference_RecentSticker.startPartialMediaReference_RecentSticker(&builder) valueType = .partialmediareferenceRecentsticker valueOffset = TelegramCore_PartialMediaReference_RecentSticker.endPartialMediaReference_RecentSticker(&builder, start: start) + case let .savedMusic(peer): + let peerOffset = peer.encodeToFlatBuffers(builder: &builder) + let start = TelegramCore_PartialMediaReference_SavedMusic.startPartialMediaReference_SavedMusic(&builder) + TelegramCore_PartialMediaReference_SavedMusic.add(peer: peerOffset, &builder) + valueType = .partialmediareferenceSavedmusic + valueOffset = TelegramCore_PartialMediaReference_SavedMusic.endPartialMediaReference_SavedMusic(&builder, start: start) } return TelegramCore_PartialMediaReference.createPartialMediaReference(&builder, valueType: valueType, valueOffset: valueOffset) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index cebf05cbd0..d9284622d8 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -145,6 +145,8 @@ public struct Namespaces { public static let cachedGroupsInCommon: Int8 = 46 public static let groupCallPersistentSettings: Int8 = 47 public static let cachedProfileGiftsCollections: Int8 = 48 + public static let cachedProfileSavedMusic: Int8 = 49 + public static let cachedChatThemes: Int8 = 50 } public struct UnorderedItemList { @@ -318,6 +320,7 @@ private enum PreferencesKeyValues: Int32 { case serverSuggestionInfo = 44 case persistentChatInterfaceData = 45 case globalPostSearchState = 46 + case savedMusicIds = 47 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -580,6 +583,12 @@ public struct PreferencesKeys { key.setInt32(0, value: PreferencesKeyValues.globalPostSearchState.rawValue) return key } + + public static func savedMusicIds() -> ValueBoxKey { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: PreferencesKeyValues.savedMusicIds.rawValue) + return key + } } private enum SharedDataKeyValues: Int32 { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_OutgoingMessageInfoAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_OutgoingMessageInfoAttribute.swift index eabe75a341..6f7c4e0220 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_OutgoingMessageInfoAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_OutgoingMessageInfoAttribute.swift @@ -21,13 +21,15 @@ public class OutgoingMessageInfoAttribute: MessageAttribute { public let acknowledged: Bool public let correlationId: Int64? public let bubbleUpEmojiOrStickersets: [ItemCollectionId] + public let partialReference: PartialMediaReference? - public init(uniqueId: Int64, flags: OutgoingMessageInfoFlags, acknowledged: Bool, correlationId: Int64?, bubbleUpEmojiOrStickersets: [ItemCollectionId]) { + public init(uniqueId: Int64, flags: OutgoingMessageInfoFlags, acknowledged: Bool, correlationId: Int64?, bubbleUpEmojiOrStickersets: [ItemCollectionId], partialReference: PartialMediaReference?) { self.uniqueId = uniqueId self.flags = flags self.acknowledged = acknowledged self.correlationId = correlationId self.bubbleUpEmojiOrStickersets = bubbleUpEmojiOrStickersets + self.partialReference = partialReference } required public init(decoder: PostboxDecoder) { @@ -40,6 +42,11 @@ public class OutgoingMessageInfoAttribute: MessageAttribute { } else { self.bubbleUpEmojiOrStickersets = [] } + if let partialReference = decoder.decodeAnyObjectForKey("partialReference", decoder: { PartialMediaReference(decoder: $0) }) as? PartialMediaReference { + self.partialReference = partialReference + } else { + self.partialReference = nil + } } public func encode(_ encoder: PostboxEncoder) { @@ -54,13 +61,18 @@ public class OutgoingMessageInfoAttribute: MessageAttribute { let bubbleUpEmojiOrStickersetsBuffer = WriteBuffer() ItemCollectionId.encodeArrayToBuffer(self.bubbleUpEmojiOrStickersets, buffer: bubbleUpEmojiOrStickersetsBuffer) encoder.encodeData(bubbleUpEmojiOrStickersetsBuffer.makeData(), forKey: "bubbleUpEmojiOrStickersets") + if let partialReference { + encoder.encodeObjectWithEncoder(partialReference, encoder: partialReference.encode, forKey: "partialReference") + } else { + encoder.encodeNil(forKey: "partialReference") + } } public func withUpdatedFlags(_ flags: OutgoingMessageInfoFlags) -> OutgoingMessageInfoAttribute { - return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: flags, acknowledged: self.acknowledged, correlationId: self.correlationId, bubbleUpEmojiOrStickersets: self.bubbleUpEmojiOrStickersets) + return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: flags, acknowledged: self.acknowledged, correlationId: self.correlationId, bubbleUpEmojiOrStickersets: self.bubbleUpEmojiOrStickersets, partialReference: self.partialReference) } public func withUpdatedAcknowledged(_ acknowledged: Bool) -> OutgoingMessageInfoAttribute { - return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: self.flags, acknowledged: acknowledged, correlationId: self.correlationId, bubbleUpEmojiOrStickersets: self.bubbleUpEmojiOrStickersets) + return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: self.flags, acknowledged: acknowledged, correlationId: self.correlationId, bubbleUpEmojiOrStickersets: self.bubbleUpEmojiOrStickersets, partialReference: self.partialReference) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index 065943289f..000e34438a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -10,13 +10,6 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { ] } - var messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]] = [:] - for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles { - messageThreadHoles[peerNamespace] = [ - Namespaces.Message.Cloud - ] - } - // To avoid upgrading the database, **new** tags can be added here // Uninitialized peers will fill the info using messageHoles var upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]] = [:] @@ -42,7 +35,15 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { ), messageHoles: messageHoles, upgradedMessageHoles: upgradedMessageHoles, - messageThreadHoles: messageThreadHoles, + messageThreadHoles: { peerIdNamespace, threadId in + if threadId == Message.newTopicThreadId { + return nil + } + if peerIdNamespacesWithInitialCloudMessageHoles.contains(peerIdNamespace) { + return [Namespaces.Message.Cloud] + } + return nil + }, existingMessageTags: MessageTags.all, messageTagsWithSummary: [.unseenPersonalMessage, .pinned, .video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file, .unseenReaction], messageTagsWithThreadSummary: [.unseenPersonalMessage, .unseenReaction], @@ -76,10 +77,14 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { peerSummaryIsThreadBased: { peer, associatedPeer in if let channel = peer as? TelegramChannel { if channel.flags.contains(.isForum) { - if channel.flags.contains(.displayForumAsTabs) { - return (false, false) - } else { + if channel.linkedBotId != nil { return (true, false) + } else { + if channel.flags.contains(.displayForumAsTabs) { + return (false, false) + } else { + return (true, false) + } } } else if channel.flags.contains(.isMonoforum) { if let associatedPeer = associatedPeer as? TelegramChannel, associatedPeer.hasPermission(.manageDirect) { @@ -183,6 +188,14 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { } return false }, + decodeAssociatedChatListPeerId: { cachedData in + if let cachedData = cachedData as? CachedUserData { + if case let .known(value) = cachedData.linkedBotChannelId { + return value + } + } + return nil + }, isPeerUpgradeMessage: { message in for media in message.media { if let action = media as? TelegramMediaAction { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index 68e9ac0cc8..fc7676a016 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -214,10 +214,13 @@ public final class TelegramChannel: Peer, Equatable { public let verificationIconFileId: Int64? public let sendPaidMessageStars: StarsAmount? public let linkedMonoforumId: PeerId? + public let linkedBotId: PeerId? public var associatedPeerId: PeerId? { if self.flags.contains(.isMonoforum) { return self.linkedMonoforumId + } else if let linkedBotId = self.linkedBotId { + return linkedBotId } else { return nil } @@ -258,7 +261,21 @@ public final class TelegramChannel: Peer, Equatable { return mediaIds } - public let notificationSettingsPeerId: PeerId? = nil + public var notificationSettingsPeerId: PeerId? { + if let linkedBotId = self.linkedBotId { + return linkedBotId + } else { + return nil + } + } + + public var associatedPeerOverridesIdentity: Bool { + if self.linkedBotId != nil { + return true + } else { + return false + } + } public var timeoutAttribute: UInt32? { if let emojiStatus = self.emojiStatus { @@ -298,7 +315,8 @@ public final class TelegramChannel: Peer, Equatable { subscriptionUntilDate: Int32?, verificationIconFileId: Int64?, sendPaidMessageStars: StarsAmount?, - linkedMonoforumId: PeerId? + linkedMonoforumId: PeerId?, + linkedBotId: PeerId? ) { self.id = id self.accessHash = accessHash @@ -326,6 +344,7 @@ public final class TelegramChannel: Peer, Equatable { self.verificationIconFileId = verificationIconFileId self.sendPaidMessageStars = sendPaidMessageStars self.linkedMonoforumId = linkedMonoforumId + self.linkedBotId = linkedBotId } public init(decoder: PostboxDecoder) { @@ -365,6 +384,7 @@ public final class TelegramChannel: Peer, Equatable { self.verificationIconFileId = decoder.decodeOptionalInt64ForKey("vfid") self.sendPaidMessageStars = decoder.decodeCodable(StarsAmount.self, forKey: "sendPaidMessageStars") self.linkedMonoforumId = decoder.decodeOptionalInt64ForKey("lmid").flatMap(PeerId.init) + self.linkedBotId = decoder.decodeOptionalInt64ForKey("lbid").flatMap(PeerId.init) #if DEBUG && false var builder = FlatBufferBuilder(initialSize: 1024) @@ -491,6 +511,12 @@ public final class TelegramChannel: Peer, Equatable { } else { encoder.encodeNil(forKey: "lmid") } + + if let linkedBotId = self.linkedBotId { + encoder.encodeInt64(linkedBotId.toInt64(), forKey: "lbid") + } else { + encoder.encodeNil(forKey: "lbid") + } } public func isEqual(_ other: Peer) -> Bool { @@ -561,67 +587,70 @@ public final class TelegramChannel: Peer, Equatable { if lhs.linkedMonoforumId != rhs.linkedMonoforumId { return false } + if lhs.linkedBotId != rhs.linkedBotId { + return false + } return true } public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedAddressNames(_ addressNames: [TelegramPeerUsername]) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedInfo(_ info: TelegramChannelInfo) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedSendPaidMessageStars(_ sendPaidMessageStars: StarsAmount?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedNameColor(_ nameColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedApproximateBoostLevel(_ approximateBoostLevel: Int32?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedSubscriptionUntilDate(_ subscriptionUntilDate: Int32?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: subscriptionUntilDate, verificationIconFileId: self.verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public func withUpdatedVerificationIconFileId(_ verificationIconFileId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel, subscriptionUntilDate: self.subscriptionUntilDate, verificationIconFileId: verificationIconFileId, sendPaidMessageStars: self.sendPaidMessageStars, linkedMonoforumId: self.linkedMonoforumId, linkedBotId: self.linkedBotId) } public init(flatBuffersObject: TelegramCore_TelegramChannel) throws { @@ -656,6 +685,7 @@ public final class TelegramChannel: Peer, Equatable { self.verificationIconFileId = flatBuffersObject.verificationIconFileId == Int64.min ? nil : flatBuffersObject.verificationIconFileId self.sendPaidMessageStars = try flatBuffersObject.sendPaidMessageStars.flatMap { try StarsAmount(flatBuffersObject: $0) } self.linkedMonoforumId = flatBuffersObject.linkedMonoforumId.flatMap { PeerId(flatBuffersObject: $0) } + self.linkedBotId = flatBuffersObject.linkedBotId.flatMap { PeerId(flatBuffersObject: $0) } } public func encodeToFlatBuffers(builder: inout FlatBufferBuilder) -> Offset { @@ -736,6 +766,9 @@ public final class TelegramChannel: Peer, Equatable { if let linkedMonoforumId = self.linkedMonoforumId { TelegramCore_TelegramChannel.add(linkedMonoforumId: linkedMonoforumId.asFlatBuffersObject(), &builder) } + if let linkedBotId = self.linkedBotId { + TelegramCore_TelegramChannel.add(linkedBotId: linkedBotId.asFlatBuffersObject(), &builder) + } return TelegramCore_TelegramChannel.endTelegramChannel(&builder, start: start) } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 51fd2c771d..028ef95a92 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -224,7 +224,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case geoProximityReached(from: PeerId, to: PeerId, distance: Int32) case groupPhoneCall(callId: Int64, accessHash: Int64, scheduleDate: Int32?, duration: Int32?) case inviteToGroupPhoneCall(callId: Int64, accessHash: Int64, peerIds: [PeerId]) - case setChatTheme(emoji: String) + case setChatTheme(chatTheme: ChatTheme) case joinedByRequest case webViewData(String) case giftPremium(currency: String, amount: Int64, months: Int32, cryptoCurrency: String?, cryptoAmount: Int64?, text: String?, entities: [MessageTextEntity]?) @@ -243,8 +243,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String) case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?) case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?) - case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?) - case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?) + case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, prepaidUpgradeHash: String?, giftMessageId: Int32?, upgradeSeparate: Bool) + case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?) case paidMessagesRefunded(count: Int32, stars: Int64) case paidMessagesPriceEdited(stars: Int64, broadcastMessagesAllowed: Bool) case conferenceCall(ConferenceCall) @@ -317,7 +317,13 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } self = .inviteToGroupPhoneCall(callId: decoder.decodeInt64ForKey("callId", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), peerIds: peerIds) case 24: - self = .setChatTheme(emoji: decoder.decodeStringForKey("emoji", orElse: "")) + if let chatThemeData = decoder.decodeDataForKey("chatTheme"), let chatTheme = try? AdaptedPostboxDecoder().decode(ChatTheme.self, from: chatThemeData) { + self = .setChatTheme(chatTheme: chatTheme) + } else if let emoji = decoder.decodeOptionalStringForKey("emoji"), !emoji.isEmpty { + self = .setChatTheme(chatTheme: .emoticon(emoji)) + } else { + self = .setChatTheme(chatTheme: .emoticon("")) + } case 25: self = .joinedByRequest case 26: @@ -375,7 +381,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } self = .prizeStars(amount: decoder.decodeInt64ForKey("amount", orElse: 0), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: boostPeerId, transactionId: decoder.decodeOptionalStringForKey("transactionId"), giveawayMessageId: giveawayMessageId) case 44: - self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId")) + self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), prepaidUpgradeHash: decoder.decodeOptionalStringForKey("prepaidUpgradeHash"), giftMessageId: decoder.decodeOptionalInt32ForKey("giftMessageId"), upgradeSeparate: decoder.decodeOptionalBoolForKey("upgradeSeparate") ?? false) case 45: var resaleAmount: CurrencyAmount? if let amount = decoder.decodeCodable(CurrencyAmount.self, forKey: "resaleAmount") { @@ -383,7 +389,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else if let stars = decoder.decodeOptionalInt64ForKey("resaleStars") { resaleAmount = CurrencyAmount(amount: StarsAmount(value: stars, nanos: 0), currency: .stars) } - self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate")) + self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate")) case 46: self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0)) case 47: @@ -534,9 +540,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeInt64(callId, forKey: "callId") encoder.encodeInt64(accessHash, forKey: "accessHash") encoder.encodeInt64Array(peerIds.map { $0.toInt64() }, forKey: "peerIds") - case let .setChatTheme(emoji): + case let .setChatTheme(chatTheme): encoder.encodeInt32(24, forKey: "_rawValue") - encoder.encodeString(emoji, forKey: "emoji") + if let chatThemeData = try? AdaptedPostboxEncoder().encode(chatTheme) { + encoder.encodeData(chatThemeData, forKey: "chatTheme") + } case .joinedByRequest: encoder.encodeInt32(25, forKey: "_rawValue") case let .webViewData(text): @@ -705,7 +713,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "giveawayMsgId") } - case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, upgradeMessageId, peerId, senderId, savedId): + case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate): encoder.encodeInt32(44, forKey: "_rawValue") encoder.encodeObject(gift, forKey: "gift") if let convertStars { @@ -731,6 +739,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeNil(forKey: "upgradeStars") } encoder.encodeBool(isRefunded, forKey: "isRefunded") + encoder.encodeBool(isPrepaidUpgrade, forKey: "isPrepaidUpgrade") if let upgradeMessageId { encoder.encodeInt32(upgradeMessageId, forKey: "upgradeMessageId") } else { @@ -751,7 +760,18 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "savedId") } - case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate): + if let prepaidUpgradeHash { + encoder.encodeString(prepaidUpgradeHash, forKey: "prepaidUpgradeHash") + } else { + encoder.encodeNil(forKey: "prepaidUpgradeHash") + } + if let giftMessageId { + encoder.encodeInt32(giftMessageId, forKey: "giftMessageId") + } else { + encoder.encodeNil(forKey: "giftMessageId") + } + encoder.encodeBool(upgradeSeparate, forKey: "upgradeSeparate") + case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate): encoder.encodeInt32(45, forKey: "_rawValue") encoder.encodeObject(gift, forKey: "gift") encoder.encodeBool(isUpgrade, forKey: "isUpgrade") @@ -768,6 +788,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeNil(forKey: "transferStars") } encoder.encodeBool(isRefunded, forKey: "isRefunded") + encoder.encodeBool(isPrepaidUpgrade, forKey: "isPrepaidUpgrade") if let peerId { encoder.encodeInt64(peerId.toInt64(), forKey: "peerId") } else { @@ -875,7 +896,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { return [peerId] case let .prizeStars(_, _, boostPeerId, _, _): return boostPeerId.flatMap { [$0] } ?? [] - case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, peerId, senderId, _): + case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _): var peerIds: [PeerId] = [] if let peerId { peerIds.append(peerId) @@ -887,7 +908,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { peerIds.append(releasedBy) } return peerIds - case let .starGiftUnique(gift, _, _, _, _, _, _, peerId, senderId, _, _, _, _): + case let .starGiftUnique(gift, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _): var peerIds: [PeerId] = [] if let peerId { peerIds.append(peerId) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift index fb5cd2970d..c6cf96b758 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift @@ -42,6 +42,7 @@ public struct BotUserInfoFlags: OptionSet { public static let canEdit = BotUserInfoFlags(rawValue: (1 << 5)) public static let isBusiness = BotUserInfoFlags(rawValue: (1 << 6)) public static let hasWebApp = BotUserInfoFlags(rawValue: (1 << 7)) + public static let hasForum = BotUserInfoFlags(rawValue: (1 << 8)) } public struct BotUserInfo: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramWallpaper.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramWallpaper.swift index 0872978f07..b96ac65480 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramWallpaper.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramWallpaper.swift @@ -230,6 +230,37 @@ public enum TelegramWallpaper: Equatable { self.file = file self.settings = settings } + + public static func ==(lhs: File, rhs: File) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.accessHash != rhs.accessHash { + return false + } + if lhs.isCreator != rhs.isCreator { + return false + } + if lhs.isDefault != rhs.isDefault { + return false + } + if lhs.isPattern != rhs.isPattern { + return false + } + if lhs.isDark != rhs.isDark { + return false + } + if lhs.slug != rhs.slug { + return false + } + if lhs.file.fileId != rhs.file.fileId { + return false + } + if lhs.settings != rhs.settings { + return false + } + return true + } } case builtin(WallpaperSettings) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift index 20b3ca184b..7a43a6dea9 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_UnauthorizedAccountState.swift @@ -182,7 +182,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool) case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool) case signUp(number: String, codeHash: String, firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, syncContacts: Bool) - case payment(number: String, codeHash: String, storeProduct: String, syncContacts: Bool) + case payment(number: String, codeHash: String, storeProduct: String, supportEmailAddress: String, supportEmailSubject: String, syncContacts: Bool) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { @@ -217,7 +217,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable case UnauthorizedAccountStateContentsValue.signUp.rawValue: self = .signUp(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), firstName: decoder.decodeStringForKey("f", orElse: ""), lastName: decoder.decodeStringForKey("l", orElse: ""), termsOfService: decoder.decodeObjectForKey("tos", decoder: { UnauthorizedAccountTermsOfService(decoder: $0) }) as? UnauthorizedAccountTermsOfService, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0) case UnauthorizedAccountStateContentsValue.payment.rawValue: - self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0) + self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""), supportEmailAddress: decoder.decodeStringForKey("supportEmailAddress", orElse: ""), supportEmailSubject: decoder.decodeStringForKey("supportEmailSubject", orElse: ""), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0) default: assertionFailure() self = .empty @@ -307,11 +307,13 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable encoder.encodeNil(forKey: "tos") } encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts") - case let .payment(number, codeHash, storeProduct, syncContacts): + case let .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts): encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v") encoder.encodeString(number, forKey: "n") encoder.encodeString(codeHash, forKey: "h") encoder.encodeString(storeProduct, forKey: "storeProduct") + encoder.encodeString(supportEmailAddress, forKey: "supportEmailAddress") + encoder.encodeString(supportEmailSubject, forKey: "supportEmailSubject") encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts") } } @@ -384,8 +386,8 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable } else { return false } - case let .payment(number, codeHash, storeProduct, syncContacts): - if case .payment(number, codeHash, storeProduct, syncContacts) = rhs { + case let .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts): + if case .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts) = rhs { return true } else { return false diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 60765b101b..b20cb92f1c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -3353,6 +3353,7 @@ func _internal_refreshInlineGroupCall(account: Account, messageId: MessageId) -> } return .update(StoreMessage( id: currentMessage.id, + customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index 6d38eb8972..305f4882f0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -209,6 +209,11 @@ public extension TelegramEngine.EngineData.Item { return nil } peers[mainChannel.id] = EnginePeer(mainChannel) + } else if let channel = peer as? TelegramChannel, channel.isForum, let linkedBotId = channel.linkedBotId { + guard let mainChannel = view.peers[linkedBotId] else { + return nil + } + peers[mainChannel.id] = EnginePeer(mainChannel) } return EngineRenderedPeer(peerId: self.id, peers: peers, associatedMedia: view.media) @@ -525,8 +530,8 @@ public extension TelegramEngine.EngineData.Item { } } - public struct ThemeEmoticon: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { - public typealias Result = Optional + public struct ChatTheme: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Optional fileprivate var id: EnginePeer.Id public var mapKey: EnginePeer.Id { @@ -549,11 +554,11 @@ public extension TelegramEngine.EngineData.Item { return nil } if let cachedData = cachedPeerData as? CachedUserData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedPeerData as? CachedGroupData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedPeerData as? CachedChannelData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else { return nil } @@ -858,6 +863,39 @@ public extension TelegramEngine.EngineData.Item { } } + public struct LinkedBotForumPeerId: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = EnginePeerCachedInfoItem + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + switch cachedData.linkedBotChannelId { + case let .known(id): + return .known(id) + case .unknown: + return .unknown + } + } else { + return .unknown + } + } + } + public struct StatusSettings: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = EnginePeer.StatusSettings? @@ -2265,6 +2303,39 @@ public extension TelegramEngine.EngineData.Item { } } + public struct CopyProtectionEnabled: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Bool + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .basicPeer(self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? BasicPeerView else { + preconditionFailure() + } + guard let peer = view.peer else { + return false + } + if let group = peer as? TelegramGroup { + return group.flags.contains(.copyProtectionEnabled) + } else if let channel = peer as? TelegramChannel { + return channel.flags.contains(.copyProtectionEnabled) + } else { + return false + } + } + } + public struct BotPreview: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = CachedUserData.BotPreview? @@ -2377,6 +2448,36 @@ public extension TelegramEngine.EngineData.Item { } } + public struct ProfileMainTab: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Optional + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.mainProfileTab + } else if let cachedData = view.cachedPeerData as? CachedChannelData { + return cachedData.mainProfileTab + } else { + return nil + } + } + } + public struct BotPrivacyPolicyUrl: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Optional @@ -2557,6 +2658,31 @@ public extension TelegramEngine.EngineData.Item { } } + public struct BotLinkedForum: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = CachedUserData.LinkedBotChannelId? + + public let id: EnginePeer.Id + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.linkedBotChannelId + } else { + return nil + } + } + } + public struct AutoTranslateEnabled: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Bool diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index bdec7f6acc..bdf6049d29 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -276,7 +276,8 @@ private class AdMessagesHistoryContextImpl { subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, - linkedMonoforumId: nil + linkedMonoforumId: nil, + linkedBotId: nil ) messagePeers[author.id] = author diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index 2b42cf7357..5ff10ef4aa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -10,7 +10,7 @@ func _internal_applyMaxReadIndexInteractively(postbox: Postbox, stateManager: Ac } } -func _internal_applyMaxReadIndexInteractively(transaction: Transaction, stateManager: AccountStateManager, index: MessageIndex) { +func _internal_applyMaxReadIndexInteractively(transaction: Transaction, stateManager: AccountStateManager, index: MessageIndex) { let messageIds = transaction.applyInteractiveReadMaxIndex(index) if let channel = transaction.getPeer(index.id.peerId) as? TelegramChannel, channel.isForumOrMonoForum { @@ -55,7 +55,7 @@ func _internal_applyMaxReadIndexInteractively(transaction: Transaction, stateMan return currentAttribute } }) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) } break @@ -123,7 +123,7 @@ func applySecretOutgoingMessageReadActions(transaction: Transaction, id: Message return currentAttribute } }) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) } break @@ -307,6 +307,12 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo } } } + + if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) { + if let cachedData = transaction.getPeerCachedData(peerId: user.id) as? CachedUserData, case let .known(linkedBotChannelId) = cachedData.linkedBotChannelId, let linkedBotChannelId { + _internal_togglePeerUnreadMarkInteractively(transaction: transaction, network: network, viewTracker: viewTracker, peerId: linkedBotChannelId, setToValue: false) + } + } } public func clearPeerUnseenPersonalMessagesInteractively(account: Account, peerId: PeerId, threadId: Int64?) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index a36e8fa7d8..a893ace4dc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -532,7 +532,7 @@ extension EngineChatList.Item { } var forumTopicDataValue: EngineChatList.ForumTopicData? - if let forumTopicData = forumTopicData { + if let forumTopicData { let id = forumTopicData.id if let forumTopicInfo = forumTopicData.info.data.get(MessageHistoryThreadData.self) { forumTopicDataValue = EngineChatList.ForumTopicData(id: id, title: forumTopicInfo.info.title, iconFileId: forumTopicInfo.info.icon, iconColor: forumTopicInfo.info.iconColor, maxOutgoingReadMessageId: MessageId(peerId: index.messageIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: forumTopicInfo.maxOutgoingReadId), isUnread: forumTopicInfo.incomingUnreadCount > 0, threadPeer: forumTopicData.threadPeer.flatMap(EnginePeer.init)) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessagesInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessagesInteractively.swift index 6fb62b8142..33bcac50a0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessagesInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessagesInteractively.swift @@ -174,7 +174,7 @@ func _internal_clearHistoryInteractively(postbox: Postbox, peerId: PeerId, threa } if addEmptyMessage { - let _ = transaction.addMessages([StoreMessage(id: topIndex.id, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random) + let _ = transaction.addMessages([StoreMessage(id: topIndex.id, customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random) } } else { updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: topIndex.timestamp, forceRootGroupIfNotExists: false) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/FactCheck.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/FactCheck.swift index 426913e2b7..bd4f8918ef 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/FactCheck.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/FactCheck.swift @@ -111,7 +111,7 @@ func _internal_getMessagesFactCheckByPeerId(account: Account, peerId: EnginePeer let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) var attributes = currentMessage.attributes.filter { !($0 is FactCheckMessageAttribute) } attributes.append(attribute) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } index += 1 diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/InstallInteractiveReadMessagesAction.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/InstallInteractiveReadMessagesAction.swift index 76bdb7be4b..2b523ae5d9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/InstallInteractiveReadMessagesAction.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/InstallInteractiveReadMessagesAction.swift @@ -67,7 +67,7 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag } tags.remove(.unseenReaction) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if consumeMessageIds.contains(id) { @@ -151,7 +151,7 @@ private final class StoreOrUpdateMessageActionImpl: StoreOrUpdateMessageAction { } var tags = currentMessage.tags tags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) transaction.setPendingMessageAction(type: .readReaction, id: id, action: ReadReactionAction()) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift index b181922619..57d89f8f5e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift @@ -120,7 +120,7 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) } } @@ -153,7 +153,7 @@ func _internal_markReactionsAsSeenInteractively(postbox: Postbox, messageId: Mes } var tags = currentMessage.tags tags.remove(.unseenReaction) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) } } @@ -244,7 +244,7 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) }) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift index 6b8e794024..7990a10d82 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Message.swift @@ -1,6 +1,8 @@ import Postbox public final class EngineMessage: Equatable { + public static let newTopicThreadId: Int64 = Message.newTopicThreadId + public typealias Id = MessageId public typealias StableId = UInt32 public typealias Index = MessageIndex diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 0ca8c13cf7..1067882935 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -233,7 +233,7 @@ private class ReplyThreadHistoryContextImpl { ) } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } @@ -343,6 +343,8 @@ private class ReplyThreadHistoryContextImpl { return (nil, nil, nil, nil) } + var markMainAsRead = false + if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { if messageIndex.id.id >= data.maxIncomingReadId { if let count = transaction.getThreadMessageCount(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) { @@ -365,9 +367,30 @@ private class ReplyThreadHistoryContextImpl { if let entry = StoredMessageHistoryThreadInfo(data) { transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry) } + + if data.incomingUnreadCount == 0, let channel = peer as? TelegramChannel, channel.linkedBotId != nil { + var hasOtherUnread = false + for item in transaction.getMessageHistoryThreadIndex(peerId: peerId, limit: 20) { + guard let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: item.threadId)?.data.get(MessageHistoryThreadData.self) else { + continue + } + if data.incomingUnreadCount != 0 { + hasOtherUnread = true + break + } + } + + if !hasOtherUnread { + markMainAsRead = true + } + } } } + if markMainAsRead { + _internal_applyMaxReadIndexInteractively(transaction: transaction, stateManager: account.stateManager, index: messageIndex) + } + var subPeerId: Api.InputPeer? if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { subPeerId = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) @@ -400,7 +423,7 @@ private class ReplyThreadHistoryContextImpl { attributes[j] = updatedAttribute } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestStartBot.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestStartBot.swift index 47fae09fa3..58f8442ce0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestStartBot.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestStartBot.swift @@ -6,6 +6,88 @@ import MtProtoKit func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: String?) -> Signal { + /*if "".isEmpty { + return account.postbox.loadedPeerWithId(botPeerId) + |> mapToSignal { botPeer -> Signal in + guard let inputUser = apiInputUser(botPeer), let botPeer = botPeer as? TelegramUser, let botInfo = botPeer.botInfo else { + return .complete() + } + if botInfo.flags.contains(.hasForum) { + return account.network.request(Api.functions.channels.createBotForum(botId: inputUser)) + |> mapToSignal { result -> Signal in + account.stateManager.addUpdates(result) + + for update in result.allUpdates { + if case let .updateChannel(channelId) = update { + let _ = (account.postbox.transaction { transaction -> Void in + transaction.updatePeerCachedData(peerIds: Set([botPeerId]), update: { _, current in + guard var current = current as? CachedUserData else { + return current + } + + current = current.withUpdatedLinkedBotChannelId(PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))) + + return current + }) + } |> delay(0.4, queue: .mainQueue())).startStandalone() + } + } + + if let payload = payload, !payload.isEmpty { + return account.postbox.loadedPeerWithId(botPeerId) + |> mapToSignal { botPeer -> Signal in + if let inputUser = apiInputUser(botPeer) { + return account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload)) + |> mapToSignal { result -> Signal in + account.stateManager.addUpdates(result) + return .complete() + } + |> `catch` { _ -> Signal in + return .complete() + } + |> retryRequest + } else { + return .complete() + } + } + |> castError(MTRpcError.self) + } else { + return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) |> mapToSignal { _ -> Signal in + return .complete() + } + |> castError(MTRpcError.self) + } + } + |> `catch` { _ -> Signal in + return .complete() + } + } + + if let payload = payload, !payload.isEmpty { + return account.postbox.loadedPeerWithId(botPeerId) + |> mapToSignal { botPeer -> Signal in + if let inputUser = apiInputUser(botPeer) { + return account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload)) + |> mapToSignal { result -> Signal in + account.stateManager.addUpdates(result) + return .complete() + } + |> `catch` { _ -> Signal in + return .complete() + } + |> retryRequest + } else { + return .complete() + } + } + } else { + return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) |> mapToSignal { _ -> Signal in + return .complete() + } + } + } + }*/ + if let payload = payload, !payload.isEmpty { return account.postbox.loadedPeerWithId(botPeerId) |> mapToSignal { botPeer -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 9085fa32e0..a75267344c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -476,7 +476,7 @@ public extension TelegramEngine { updatedMedia[i] = TelegramMediaMap(latitude: media.latitude, longitude: media.longitude, heading: media.heading, accuracyRadius: media.accuracyRadius, venue: media.venue, liveBroadcastingTimeout: max(0, timestamp - currentMessage.timestamp - 1), liveProximityNotificationRadius: nil) } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia)) }) } } @@ -616,7 +616,7 @@ public extension TelegramEngine { attributes.append(AudioTranscriptionMessageAttribute(id: 0, text: text, isPending: !isFinal, didRate: false, error: error)) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues @@ -632,7 +632,7 @@ public extension TelegramEngine { attributes.append(DerivedDataMessageAttribute(data: data)) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues @@ -654,7 +654,7 @@ public extension TelegramEngine { attributes.append(DerivedDataMessageAttribute(data: data)) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } |> ignoreValues diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Todo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Todo.swift index 6d4c3e0a47..527e90d7b6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Todo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Todo.swift @@ -26,7 +26,7 @@ func _internal_requestUpdateTodoMessageItems(account: Account, messageId: Messag updatedCompletions.removeAll(where: { incompletedIds.contains($0.id) }) media = [todo.withUpdated(completions: updatedCompletions)] } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) }) return account.network.request(Api.functions.messages.toggleTodoCompleted(peer: inputPeer, msgId: messageId.id, completed: completedIds, incompleted: incompletedIds)) |> mapError { _ -> RequestUpdateTodoMessageError in @@ -59,7 +59,7 @@ func _internal_appendTodoMessageItems(account: Account, messageId: MessageId, it updatedItems.append(contentsOf: items) media = [todo.withUpdated(items: updatedItems)] } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) }) return account.network.request(Api.functions.messages.appendTodoList(peer: inputPeer, msgId: messageId.id, list: items.map { $0.apiItem })) |> mapError { _ -> AppendTodoMessageError in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift index ec7911a7b4..f96a0dcd5e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Transcription.swift @@ -83,7 +83,7 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: Me attributes.append(updatedAttribute) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if updatedAttribute.error == nil { @@ -111,6 +111,7 @@ func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messag } return .update(StoreMessage( id: currentMessage.id, + customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index 02b5519c5d..aaaaed5e05 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -212,7 +212,7 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin attributes.append(updatedAttribute) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } index += 1 @@ -246,7 +246,7 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin let updatedAttribute: TranslationMessageAttribute = TranslationMessageAttribute(text: title.0, entities: title.1, additional: attrOptions, pollSolution: solution, toLang: toLang) attributes.append(updatedAttribute) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/UpdatePinnedMessage.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/UpdatePinnedMessage.swift index 5874962f80..69277048bc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/UpdatePinnedMessage.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/UpdatePinnedMessage.swift @@ -95,7 +95,7 @@ func _internal_requestUpdatePinnedMessage(account: Account, peerId: PeerId, upda if updatedTags == currentMessage.tags { return .skip } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } @@ -204,7 +204,7 @@ func _internal_requestUnpinAllMessages(account: Account, peerId: PeerId, threadI return .skip } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift index c89af92e93..9276796244 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift @@ -17,7 +17,7 @@ public enum AppStoreTransactionPurpose { case gift(peerId: EnginePeer.Id, currency: String, amount: Int64) case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?, currency: String, amount: Int64, text: String?, entities: [MessageTextEntity]?) case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64) - case stars(count: Int64, currency: String, amount: Int64) + case stars(count: Int64, currency: String, amount: Int64, peerId: EnginePeer.Id?) case starsGift(peerId: EnginePeer.Id, count: Int64, currency: String, amount: Int64) case starsGiveaway(stars: Int64, boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, users: Int32) case authCode(restore: Bool, phoneNumber: String, phoneCodeHash: String, currency: String, amount: Int64) @@ -99,10 +99,26 @@ private func apiInputStorePaymentPurpose(postbox: Postbox, purpose: AppStoreTran return .single(.inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount)) } |> switchToLatest - case let .stars(count, currency, amount): - return .single(.inputStorePaymentStarsTopup(stars: count, currency: currency, amount: amount)) + case let .stars(count, currency, amount, peerId): + let peerSignal: Signal + if let peerId { + peerSignal = postbox.loadedPeerWithId(peerId) + |> map { peer in + return apiInputPeer(peer) + } + } else { + peerSignal = .single(nil) + } + return peerSignal + |> map { spendPurposePeer in + var flags: Int32 = 0 + if let _ = spendPurposePeer { + flags |= (1 << 0) + } + return .inputStorePaymentStarsTopup(flags: flags, stars: count, currency: currency, amount: amount, spendPurposePeer: spendPurposePeer) + } case let .starsGift(peerId, count, currency, amount): - return postbox.loadedPeerWithId(peerId) + return postbox.loadedPeerWithId(peerId) |> mapToSignal { peer -> Signal in guard let inputUser = apiInputUser(peer) else { return .complete() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index 066228e8d8..11194a5e51 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -9,7 +9,7 @@ public enum BotPaymentInvoiceSource { case slug(String) case premiumGiveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, option: PremiumGiftCodeOption) case giftCode(users: [PeerId], currency: String, amount: Int64, option: PremiumGiftCodeOption, text: String?, entities: [MessageTextEntity]?) - case stars(option: StarsTopUpOption) + case stars(option: StarsTopUpOption, peerId: EnginePeer.Id?) case starsGift(peerId: EnginePeer.Id, count: Int64, currency: String, amount: Int64) case starsChatSubscription(hash: String) case starsGiveaway(stars: Int64, boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, users: Int32) @@ -18,6 +18,7 @@ public enum BotPaymentInvoiceSource { case starGiftTransfer(reference: StarGiftReference, toPeerId: EnginePeer.Id) case premiumGift(peerId: EnginePeer.Id, option: CachedPremiumGiftOption, text: String?, entities: [MessageTextEntity]?) case starGiftResale(slug: String, toPeerId: EnginePeer.Id, ton: Bool) + case starGiftPrepaidUpgrade(peerId: EnginePeer.Id, hash: String) } public struct BotPaymentInvoiceFields: OptionSet { @@ -324,12 +325,14 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv let option: Api.PremiumGiftCodeOption = .premiumGiftCodeOption(flags: flags, users: option.users, months: option.months, storeProduct: option.storeProductId, storeQuantity: option.storeQuantity, currency: option.currency, amount: option.amount) return .inputInvoicePremiumGiftCode(purpose: inputPurpose, option: option) - case let .stars(option): + case let .stars(option, peerId): var flags: Int32 = 0 - if let _ = option.storeProductId { + var spendPurposePeer: Api.InputPeer? + if let peerId, let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { flags |= (1 << 0) + spendPurposePeer = inputPeer } - return .inputInvoiceStars(purpose: .inputStorePaymentStarsTopup(stars: option.count, currency: option.currency, amount: option.amount)) + return .inputInvoiceStars(purpose: .inputStorePaymentStarsTopup(flags: flags, stars: option.count, currency: option.currency, amount: option.amount, spendPurposePeer: spendPurposePeer)) case let .starsGift(peerId, count, currency, amount): guard let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) else { return nil @@ -412,6 +415,11 @@ func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInv flags |= 1 << 0 } return .inputInvoiceStarGiftResale(flags: flags, slug: slug, toId: inputPeer) + case let .starGiftPrepaidUpgrade(peerId, hash): + guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else { + return nil + } + return .inputInvoiceStarGiftPrepaidUpgrade(peer: inputPeer, hash: hash) } } @@ -753,7 +761,7 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa receiptMessageId = id } } - case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale: + case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale, .starGiftPrepaidUpgrade: receiptMessageId = nil } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 286640e38c..055890e327 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -57,6 +57,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { case upgradeStars case releasedBy case perUserLimit + case lockedUntilDate } public struct Availability: Equatable, Codable, PostboxCoding { @@ -179,8 +180,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding { public let upgradeStars: Int64? public let releasedBy: EnginePeer.Id? public let perUserLimit: PerUserLimit? + public let lockedUntilDate: Int32? - public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?, perUserLimit: PerUserLimit?) { + public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?, perUserLimit: PerUserLimit?, lockedUntilDate: Int32?) { self.id = id self.title = title self.file = file @@ -192,6 +194,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.upgradeStars = upgradeStars self.releasedBy = releasedBy self.perUserLimit = perUserLimit + self.lockedUntilDate = lockedUntilDate } public init(from decoder: Decoder) throws { @@ -213,6 +216,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.upgradeStars = try container.decodeIfPresent(Int64.self, forKey: .upgradeStars) self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy) self.perUserLimit = try container.decodeIfPresent(PerUserLimit.self, forKey: .perUserLimit) + self.lockedUntilDate = try container.decodeIfPresent(Int32.self, forKey: .lockedUntilDate) } public init(decoder: PostboxDecoder) { @@ -227,6 +231,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.upgradeStars = decoder.decodeOptionalInt64ForKey(CodingKeys.upgradeStars.rawValue) self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) } self.perUserLimit = decoder.decodeObjectForKey(CodingKeys.perUserLimit.rawValue, decoder: { StarGift.Gift.PerUserLimit(decoder: $0) }) as? StarGift.Gift.PerUserLimit + self.lockedUntilDate = decoder.decodeOptionalInt32ForKey(CodingKeys.lockedUntilDate.rawValue) } public func encode(to encoder: Encoder) throws { @@ -247,6 +252,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { try container.encodeIfPresent(self.upgradeStars, forKey: .upgradeStars) try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy) try container.encodeIfPresent(self.perUserLimit, forKey: .perUserLimit) + try container.encodeIfPresent(self.lockedUntilDate, forKey: .lockedUntilDate) } public func encode(_ encoder: PostboxEncoder) { @@ -285,12 +291,18 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } else { encoder.encodeNil(forKey: CodingKeys.perUserLimit.rawValue) } + if let lockedUntilDate = self.lockedUntilDate { + encoder.encodeInt32(lockedUntilDate, forKey: CodingKeys.lockedUntilDate.rawValue) + } else { + encoder.encodeNil(forKey: CodingKeys.lockedUntilDate.rawValue) + } } } public struct UniqueGift: Equatable, Codable, PostboxCoding { enum CodingKeys: String, CodingKey { case id + case giftId case title case number case slug @@ -304,6 +316,20 @@ public enum StarGift: Equatable, Codable, PostboxCoding { case resellAmounts case resellForTonOnly case releasedBy + case valueAmount + case valueCurrency + case flags + case themePeerId + } + + public struct Flags: OptionSet { + public var rawValue: Int32 + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + public static let isThemeAvailable = Flags(rawValue: 1 << 0) } public enum Attribute: Equatable, Codable, PostboxCoding { @@ -543,12 +569,30 @@ public enum StarGift: Equatable, Codable, PostboxCoding { return nil } } + + public struct ValueInfo: Equatable { + public let isLastSaleOnFragment: Bool + public let valueIsAverage: Bool + public let value: Int64 + public let currency: String + public let initialSaleDate: Int32 + public let initialSaleStars: Int64 + public let initialSalePrice: Int64 + public let lastSaleDate: Int32? + public let lastSalePrice: Int64? + public let floorPrice: Int64? + public let averagePrice: Int64? + public let listedCount: Int32? + public let fragmentListedCount: Int32? + public let fragmentListedUrl: String? + } public enum DecodingError: Error { case generic } public let id: Int64 + public let giftId: Int64 public let title: String public let number: Int32 public let slug: String @@ -559,9 +603,14 @@ public enum StarGift: Equatable, Codable, PostboxCoding { public let resellAmounts: [CurrencyAmount]? public let resellForTonOnly: Bool public let releasedBy: EnginePeer.Id? + public let valueAmount: Int64? + public let valueCurrency: String? + public let flags: Flags + public let themePeerId: EnginePeer.Id? - public init(id: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?) { + public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, flags: Flags, themePeerId: EnginePeer.Id?) { self.id = id + self.giftId = giftId self.title = title self.number = number self.slug = slug @@ -572,11 +621,16 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.resellAmounts = resellAmounts self.resellForTonOnly = resellForTonOnly self.releasedBy = releasedBy + self.valueAmount = valueAmount + self.valueCurrency = valueCurrency + self.flags = flags + self.themePeerId = themePeerId } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(Int64.self, forKey: .id) + self.giftId = try container.decode(Int64.self, forKey: .giftId) self.title = try container.decode(String.self, forKey: .title) self.number = try container.decode(Int32.self, forKey: .number) self.slug = try container.decodeIfPresent(String.self, forKey: .slug) ?? "" @@ -601,10 +655,15 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } self.resellForTonOnly = try container.decodeIfPresent(Bool.self, forKey: .resellForTonOnly) ?? false self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy) + self.valueAmount = try container.decodeIfPresent(Int64.self, forKey: .valueAmount) + self.valueCurrency = try container.decodeIfPresent(String.self, forKey: .valueCurrency) + self.flags = try container.decodeIfPresent(Int32.self, forKey: .flags).flatMap { Flags(rawValue: $0) } ?? [] + self.themePeerId = try container.decodeIfPresent(Int64.self, forKey: .themePeerId).flatMap { EnginePeer.Id($0) } } public init(decoder: PostboxDecoder) { self.id = decoder.decodeInt64ForKey(CodingKeys.id.rawValue, orElse: 0) + self.giftId = decoder.decodeInt64ForKey(CodingKeys.giftId.rawValue, orElse: 0) self.title = decoder.decodeStringForKey(CodingKeys.title.rawValue, orElse: "") self.number = decoder.decodeInt32ForKey(CodingKeys.number.rawValue, orElse: 0) self.slug = decoder.decodeStringForKey(CodingKeys.slug.rawValue, orElse: "") @@ -629,11 +688,16 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } self.resellForTonOnly = decoder.decodeBoolForKey(CodingKeys.resellForTonOnly.rawValue, orElse: false) self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) } + self.valueAmount = decoder.decodeOptionalInt64ForKey(CodingKeys.valueAmount.rawValue) + self.valueCurrency = decoder.decodeOptionalStringForKey(CodingKeys.valueCurrency.rawValue) + self.flags = decoder.decodeOptionalInt32ForKey(CodingKeys.flags.rawValue).flatMap { Flags(rawValue: $0) } ?? [] + self.themePeerId = decoder.decodeOptionalInt64ForKey(CodingKeys.themePeerId.rawValue).flatMap { EnginePeer.Id($0) } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) + try container.encode(self.giftId, forKey: .giftId) try container.encode(self.title, forKey: .title) try container.encode(self.number, forKey: .number) try container.encode(self.slug, forKey: .slug) @@ -651,10 +715,15 @@ public enum StarGift: Equatable, Codable, PostboxCoding { try container.encodeIfPresent(self.resellAmounts, forKey: .resellAmounts) try container.encode(self.resellForTonOnly, forKey: .resellForTonOnly) try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy) + try container.encodeIfPresent(self.valueAmount, forKey: .valueAmount) + try container.encodeIfPresent(self.valueCurrency, forKey: .valueCurrency) + try container.encode(self.flags.rawValue, forKey: .flags) + try container.encodeIfPresent(self.themePeerId?.toInt64(), forKey: .themePeerId) } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt64(self.id, forKey: CodingKeys.id.rawValue) + encoder.encodeInt64(self.giftId, forKey: CodingKeys.giftId.rawValue) encoder.encodeString(self.title, forKey: CodingKeys.title.rawValue) encoder.encodeInt32(self.number, forKey: CodingKeys.number.rawValue) encoder.encodeString(self.slug, forKey: CodingKeys.slug.rawValue) @@ -684,11 +753,25 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } else { encoder.encodeNil(forKey: CodingKeys.releasedBy.rawValue) } + if let valueAmount = self.valueAmount, let valueCurrency = self.valueCurrency { + encoder.encodeInt64(valueAmount, forKey: CodingKeys.valueAmount.rawValue) + encoder.encodeString(valueCurrency, forKey: CodingKeys.valueCurrency.rawValue) + } else { + encoder.encodeNil(forKey: CodingKeys.valueAmount.rawValue) + encoder.encodeNil(forKey: CodingKeys.valueCurrency.rawValue) + } + encoder.encodeInt32(self.flags.rawValue, forKey: CodingKeys.flags.rawValue) + if let themePeerId = self.themePeerId { + encoder.encodeInt64(themePeerId.toInt64(), forKey: CodingKeys.themePeerId.rawValue) + } else { + encoder.encodeNil(forKey: CodingKeys.themePeerId.rawValue) + } } public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift { return UniqueGift( id: self.id, + giftId: self.giftId, title: self.title, number: self.number, slug: self.slug, @@ -698,13 +781,18 @@ public enum StarGift: Equatable, Codable, PostboxCoding { giftAddress: self.giftAddress, resellAmounts: resellAmounts, resellForTonOnly: self.resellForTonOnly, - releasedBy: self.releasedBy + releasedBy: self.releasedBy, + valueAmount: self.valueAmount, + valueCurrency: self.valueCurrency, + flags: self.flags, + themePeerId: self.themePeerId ) } public func withResellForTonOnly(_ resellForTonOnly: Bool) -> UniqueGift { return UniqueGift( id: self.id, + giftId: self.giftId, title: self.title, number: self.number, slug: self.slug, @@ -714,7 +802,32 @@ public enum StarGift: Equatable, Codable, PostboxCoding { giftAddress: self.giftAddress, resellAmounts: self.resellAmounts, resellForTonOnly: resellForTonOnly, - releasedBy: self.releasedBy + releasedBy: self.releasedBy, + valueAmount: self.valueAmount, + valueCurrency: self.valueCurrency, + flags: self.flags, + themePeerId: self.themePeerId + ) + } + + public func withThemePeerId(_ themePeerId: EnginePeer.Id?) -> UniqueGift { + return UniqueGift( + id: self.id, + giftId: self.giftId, + title: self.title, + number: self.number, + slug: self.slug, + owner: self.owner, + attributes: self.attributes, + availability: self.availability, + giftAddress: self.giftAddress, + resellAmounts: self.resellAmounts, + resellForTonOnly: self.resellForTonOnly, + releasedBy: self.releasedBy, + valueAmount: self.valueAmount, + valueCurrency: self.valueCurrency, + flags: self.flags, + themePeerId: themePeerId ) } } @@ -794,7 +907,7 @@ public extension StarGift { extension StarGift { init?(apiStarGift: Api.StarGift) { switch apiStarGift { - case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, perUserTotal, perUserRemains): + case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, perUserTotal, perUserRemains, lockedUntilDate): var flags = StarGift.Gift.Flags() if (apiFlags & (1 << 2)) != 0 { flags.insert(.isBirthdayGift) @@ -823,8 +936,8 @@ extension StarGift { guard let file = telegramMediaFileFromApiDocument(sticker, altDocuments: nil) else { return nil } - self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit)) - case let .starGiftUnique(flags, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy): + self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit, lockedUntilDate: lockedUntilDate)) + case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, themePeer): let owner: StarGift.UniqueGift.Owner if let ownerAddress { owner = .address(ownerAddress) @@ -836,7 +949,11 @@ extension StarGift { return nil } let resellAmounts = resellAmounts?.compactMap { CurrencyAmount(apiAmount: $0) } - self = .unique(StarGift.UniqueGift(id: id, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (flags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId)) + var flags = StarGift.UniqueGift.Flags() + if (apiFlags & (1 << 9)) != 0 { + flags.insert(.isThemeAvailable) + } + self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (apiFlags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency, flags: flags, themePeerId: themePeer?.peerId)) } } } @@ -1108,7 +1225,7 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star case let .updateNewMessage(message, _, _): if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: false) { for media in message.media { - if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, peerId, _, savedId, _, canTransferDate, canResaleDate) = action.action, case let .Id(messageId) = message.id { + if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate) = action.action, case let .Id(messageId) = message.id { let reference: StarGiftReference if let peerId, let savedId { reference = .peer(peerId: peerId, id: savedId) @@ -1132,7 +1249,9 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star transferStars: transferStars, canTransferDate: canTransferDate, canResaleDate: canResaleDate, - collectionIds: nil + collectionIds: nil, + prepaidUpgradeHash: nil, + upgradeSeparate: false )) } } @@ -1164,6 +1283,34 @@ func _internal_starGiftUpgradePreview(account: Account, giftId: Int64) -> Signal } } +public enum CanSendGiftResult { + case available + case unavailable(text: String, entities: [MessageTextEntity]) + case failed +} + +func _internal_checkCanSendStarGift(account: Account, giftId: Int64) -> Signal { + return account.network.request(Api.functions.payments.checkCanSendGift(giftId: giftId)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result in + guard let result else { + return .unavailable(text: "", entities: []) + } + switch result { + case .checkCanSendGiftResultOk: + return .available + case let .checkCanSendGiftResultFail(reason): + switch reason { + case let .textWithEntities(text, entities): + return .unavailable(text: text, entities: messageTextEntitiesFromApiEntities(entities)) + } + } + } +} + final class CachedProfileGifts: Codable { enum CodingKeys: String, CodingKey { case gifts @@ -1232,6 +1379,7 @@ private final class ProfileGiftsContextImpl { private var sorting: ProfileGiftsContext.Sorting private var filter: ProfileGiftsContext.Filters + private var limit: Int32 private var gifts: [ProfileGiftsContext.State.StarGift] = [] private var count: Int32? @@ -1255,7 +1403,8 @@ private final class ProfileGiftsContextImpl { peerId: EnginePeer.Id, collectionId: Int32?, sorting: ProfileGiftsContext.Sorting, - filter: ProfileGiftsContext.Filters + filter: ProfileGiftsContext.Filters, + limit: Int32 ) { self.queue = queue self.account = account @@ -1263,6 +1412,7 @@ private final class ProfileGiftsContextImpl { self.collectionId = collectionId self.sorting = sorting self.filter = filter + self.limit = limit self.loadMore() } @@ -1287,6 +1437,7 @@ private final class ProfileGiftsContextImpl { let postbox = self.account.postbox let filter = self.filter let sorting = self.sorting + let limit = self.limit let isFiltered = self.filter != .All || self.sorting != .date if !isFiltered { @@ -1297,149 +1448,149 @@ private final class ProfileGiftsContextImpl { let dataState = isFiltered ? self.filteredDataState : self.dataState - if case let .ready(true, initialNextOffset) = dataState { - if !isFiltered || isUniqueOnlyFilter, self.gifts.isEmpty, initialNextOffset == nil, !reload { - self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in - let cachedGifts = transaction.retrieveItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId))?.get(CachedProfileGifts.self) - cachedGifts?.render(transaction: transaction) - return cachedGifts - } |> deliverOn(self.queue)).start(next: { [weak self] cachedGifts in - guard let self, let cachedGifts else { - return - } - if isUniqueOnlyFilter, case .loading = self.filteredDataState { - var gifts = cachedGifts.gifts - if isUniqueOnlyFilter { - gifts = gifts.filter({ gift in - if case .unique = gift.gift { - return true - } else { - return false - } - }) - } - self.gifts = gifts - self.count = cachedGifts.count - self.notificationsEnabled = cachedGifts.notificationsEnabled - self.pushState() - } else if case .loading = self.dataState { - self.gifts = cachedGifts.gifts - self.count = cachedGifts.count - self.notificationsEnabled = cachedGifts.notificationsEnabled - self.pushState() - } - })) - } - - if isFiltered { - self.filteredDataState = .loading - } else { - self.dataState = .loading - } - if !reload { - self.pushState() - } - - let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in - return transaction.getPeer(peerId).flatMap(apiInputPeer) - } - |> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in - guard let inputPeer else { - return .single(([], 0, nil, nil)) - } - var flags: Int32 = 0 - if let _ = collectionId { - flags |= (1 << 6) - } - if case .value = sorting { - flags |= (1 << 5) - } - if !filter.contains(.hidden) { - flags |= (1 << 0) - } - if !filter.contains(.displayed) { - flags |= (1 << 1) - } - if !filter.contains(.unlimited) { - flags |= (1 << 2) - } - if !filter.contains(.limited) { - flags |= (1 << 3) - } - if !filter.contains(.unique) { - flags |= (1 << 4) - } - return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, collectionId: collectionId, offset: initialNextOffset ?? "", limit: 36)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in - guard let result else { - return .single(([], 0, nil, nil)) - } - return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in - switch result { - case let .savedStarGifts(_, count, apiNotificationsEnabled, apiGifts, nextOffset, chats, users): - let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) - - var notificationsEnabled: Bool? - if let apiNotificationsEnabled { - if case .boolTrue = apiNotificationsEnabled { - notificationsEnabled = true - } else { - notificationsEnabled = false - } - } - - let gifts = apiGifts.compactMap { ProfileGiftsContext.State.StarGift(apiSavedStarGift: $0, peerId: peerId, transaction: transaction) } - return (gifts, count, nextOffset, notificationsEnabled) - } - } - } - } - - self.disposable.set((signal - |> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset, notificationsEnabled) in - guard let self else { + guard case let .ready(true, initialNextOffset) = dataState else { + return + } + if !isFiltered || isUniqueOnlyFilter, self.gifts.isEmpty, initialNextOffset == nil, !reload { + self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in + let cachedGifts = transaction.retrieveItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId))?.get(CachedProfileGifts.self) + cachedGifts?.render(transaction: transaction) + return cachedGifts + } |> deliverOn(self.queue)).start(next: { [weak self] cachedGifts in + guard let self, let cachedGifts else { return } - if isFiltered { - if initialNextOffset == nil || reload { - self.filteredGifts = gifts - } else { - for gift in gifts { - self.filteredGifts.append(gift) - } - } - - let updatedCount = max(Int32(self.filteredGifts.count), count) - self.filteredCount = updatedCount - self.filteredDataState = .ready(canLoadMore: count != 0 && updatedCount > self.filteredGifts.count && nextOffset != nil, nextOffset: nextOffset) - } else { - if initialNextOffset == nil || reload { - self.gifts = gifts - self.cacheDisposable.set(self.account.postbox.transaction { transaction in - if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count, notificationsEnabled: notificationsEnabled)) { - transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) + if isUniqueOnlyFilter, case .loading = self.filteredDataState { + var gifts = cachedGifts.gifts + if isUniqueOnlyFilter { + gifts = gifts.filter({ gift in + if case .unique = gift.gift { + return true + } else { + return false } - }.start()) - } else { - for gift in gifts { - self.gifts.append(gift) - } + }) } - - let updatedCount = max(Int32(self.gifts.count), count) - self.count = updatedCount - self.dataState = .ready(canLoadMore: count != 0 && updatedCount > self.gifts.count && nextOffset != nil, nextOffset: nextOffset) + self.gifts = gifts + self.count = cachedGifts.count + self.notificationsEnabled = cachedGifts.notificationsEnabled + self.pushState() + } else if case .loading = self.dataState { + self.gifts = cachedGifts.gifts + self.count = cachedGifts.count + self.notificationsEnabled = cachedGifts.notificationsEnabled + self.pushState() } - - self.notificationsEnabled = notificationsEnabled - self.pushState() })) } + + if isFiltered { + self.filteredDataState = .loading + } else { + self.dataState = .loading + } + if !reload { + self.pushState() + } + + let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in + guard let inputPeer else { + return .single(([], 0, nil, nil)) + } + var flags: Int32 = 0 + if let _ = collectionId { + flags |= (1 << 6) + } + if case .value = sorting { + flags |= (1 << 5) + } + if !filter.contains(.hidden) { + flags |= (1 << 0) + } + if !filter.contains(.displayed) { + flags |= (1 << 1) + } + if !filter.contains(.unlimited) { + flags |= (1 << 2) + } + if !filter.contains(.limitedUpgradable) { + flags |= (1 << 7) + } + if !filter.contains(.limitedNonUpgradable) { + flags |= (1 << 8) + } + if !filter.contains(.unique) { + flags |= (1 << 4) + } + return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, collectionId: collectionId, offset: initialNextOffset ?? "", limit: limit)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in + guard let result else { + return .single(([], 0, nil, nil)) + } + return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in + switch result { + case let .savedStarGifts(_, count, apiNotificationsEnabled, apiGifts, nextOffset, chats, users): + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + var notificationsEnabled: Bool? + if let apiNotificationsEnabled { + if case .boolTrue = apiNotificationsEnabled { + notificationsEnabled = true + } else { + notificationsEnabled = false + } + } + + let gifts = apiGifts.compactMap { ProfileGiftsContext.State.StarGift(apiSavedStarGift: $0, peerId: peerId, transaction: transaction) } + return (gifts, count, nextOffset, notificationsEnabled) + } + } + } + } + + self.disposable.set((signal + |> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset, notificationsEnabled) in + guard let self else { + return + } + if isFiltered { + if initialNextOffset == nil || reload { + self.filteredGifts = gifts + } else { + self.filteredGifts.append(contentsOf: gifts) + } + + let updatedCount = max(Int32(self.filteredGifts.count), count) + self.filteredCount = updatedCount + self.filteredDataState = .ready(canLoadMore: count != 0 && updatedCount > self.filteredGifts.count && nextOffset != nil, nextOffset: nextOffset) + } else { + if initialNextOffset == nil || reload { + self.gifts = gifts + self.cacheDisposable.set(self.account.postbox.transaction { transaction in + if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count, notificationsEnabled: notificationsEnabled)) { + transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) + } + }.start()) + } else { + self.gifts.append(contentsOf: gifts) + } + + let updatedCount = max(Int32(self.gifts.count), count) + self.count = updatedCount + self.dataState = .ready(canLoadMore: count != 0 && updatedCount > self.gifts.count && nextOffset != nil, nextOffset: nextOffset) + } + + self.notificationsEnabled = notificationsEnabled + self.pushState() + })) } func updateStarGiftAddedToProfile(reference: StarGiftReference, added: Bool) { @@ -1948,13 +2099,14 @@ public final class ProfileGiftsContext { } public static let unlimited = Filters(rawValue: 1 << 0) - public static let limited = Filters(rawValue: 1 << 1) - public static let unique = Filters(rawValue: 1 << 2) - public static let displayed = Filters(rawValue: 1 << 3) - public static let hidden = Filters(rawValue: 1 << 4) + public static let limitedUpgradable = Filters(rawValue: 1 << 1) + public static let limitedNonUpgradable = Filters(rawValue: 1 << 2) + public static let unique = Filters(rawValue: 1 << 3) + public static let displayed = Filters(rawValue: 1 << 4) + public static let hidden = Filters(rawValue: 1 << 5) public static var All: Filters { - return [.unlimited, .limited, .unique, .displayed, .hidden] + return [.unlimited, .limitedUpgradable, .limitedNonUpgradable, .unique, .displayed, .hidden] } } @@ -1985,6 +2137,8 @@ public final class ProfileGiftsContext { case canTransferDate case canResaleDate case collectionIds + case prepaidUpgradeHash + case upgradeSeparate } public let gift: TelegramCore.StarGift @@ -2004,6 +2158,8 @@ public final class ProfileGiftsContext { public let canTransferDate: Int32? public let canResaleDate: Int32? public let collectionIds: [Int32]? + public let prepaidUpgradeHash: String? + public let upgradeSeparate: Bool fileprivate let _fromPeerId: EnginePeer.Id? @@ -2028,7 +2184,9 @@ public final class ProfileGiftsContext { transferStars: Int64?, canTransferDate: Int32?, canResaleDate: Int32?, - collectionIds: [Int32]? + collectionIds: [Int32]?, + prepaidUpgradeHash: String?, + upgradeSeparate: Bool ) { self.gift = gift self.reference = reference @@ -2048,6 +2206,8 @@ public final class ProfileGiftsContext { self.canTransferDate = canTransferDate self.canResaleDate = canResaleDate self.collectionIds = collectionIds + self.prepaidUpgradeHash = prepaidUpgradeHash + self.upgradeSeparate = upgradeSeparate } public init(from decoder: Decoder) throws { @@ -2077,6 +2237,8 @@ public final class ProfileGiftsContext { self.canTransferDate = try container.decodeIfPresent(Int32.self, forKey: .canTransferDate) self.canResaleDate = try container.decodeIfPresent(Int32.self, forKey: .canResaleDate) self.collectionIds = try container.decodeIfPresent([Int32].self, forKey: .collectionIds) + self.prepaidUpgradeHash = try container.decodeIfPresent(String.self, forKey: .prepaidUpgradeHash) + self.upgradeSeparate = try container.decodeIfPresent(Bool.self, forKey: .upgradeSeparate) ?? false } public func encode(to encoder: Encoder) throws { @@ -2099,6 +2261,8 @@ public final class ProfileGiftsContext { try container.encodeIfPresent(self.canTransferDate, forKey: .canTransferDate) try container.encodeIfPresent(self.canResaleDate, forKey: .canResaleDate) try container.encodeIfPresent(self.collectionIds, forKey: .collectionIds) + try container.encodeIfPresent(self.prepaidUpgradeHash, forKey: .prepaidUpgradeHash) + try container.encode(self.upgradeSeparate, forKey: .upgradeSeparate) } public func withGift(_ gift: TelegramCore.StarGift) -> StarGift { @@ -2119,7 +2283,9 @@ public final class ProfileGiftsContext { transferStars: self.transferStars, canTransferDate: self.canTransferDate, canResaleDate: self.canResaleDate, - collectionIds: self.collectionIds + collectionIds: self.collectionIds, + prepaidUpgradeHash: self.prepaidUpgradeHash, + upgradeSeparate: self.upgradeSeparate ) } @@ -2141,7 +2307,9 @@ public final class ProfileGiftsContext { transferStars: self.transferStars, canTransferDate: self.canTransferDate, canResaleDate: self.canResaleDate, - collectionIds: self.collectionIds + collectionIds: self.collectionIds, + prepaidUpgradeHash: self.prepaidUpgradeHash, + upgradeSeparate: self.upgradeSeparate ) } @@ -2163,7 +2331,9 @@ public final class ProfileGiftsContext { transferStars: self.transferStars, canTransferDate: self.canTransferDate, canResaleDate: self.canResaleDate, - collectionIds: self.collectionIds + collectionIds: self.collectionIds, + prepaidUpgradeHash: self.prepaidUpgradeHash, + upgradeSeparate: self.upgradeSeparate ) } fileprivate func withFromPeer(_ fromPeer: EnginePeer?) -> StarGift { @@ -2184,7 +2354,9 @@ public final class ProfileGiftsContext { transferStars: self.transferStars, canTransferDate: self.canTransferDate, canResaleDate: self.canResaleDate, - collectionIds: self.collectionIds + collectionIds: self.collectionIds, + prepaidUpgradeHash: self.prepaidUpgradeHash, + upgradeSeparate: self.upgradeSeparate ) } @@ -2206,7 +2378,9 @@ public final class ProfileGiftsContext { transferStars: self.transferStars, canTransferDate: self.canTransferDate, canResaleDate: self.canResaleDate, - collectionIds: collectionIds + collectionIds: collectionIds, + prepaidUpgradeHash: self.prepaidUpgradeHash, + upgradeSeparate: self.upgradeSeparate ) } } @@ -2216,7 +2390,6 @@ public final class ProfileGiftsContext { case ready(canLoadMore: Bool, nextOffset: String?) } - public var filter: Filters public var sorting: Sorting public var gifts: [ProfileGiftsContext.State.StarGift] @@ -2251,14 +2424,15 @@ public final class ProfileGiftsContext { peerId: EnginePeer.Id, collectionId: Int32? = nil, sorting: ProfileGiftsContext.Sorting = .date, - filter: ProfileGiftsContext.Filters = .All + filter: ProfileGiftsContext.Filters = .All, + limit: Int32 = 36 ) { self.peerId = peerId self.collectionId = collectionId let queue = self.queue self.impl = QueueLocalObject(queue: queue, generate: { - return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, collectionId: collectionId, sorting: sorting, filter: filter) + return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, collectionId: collectionId, sorting: sorting, filter: filter, limit: limit) }) } @@ -2410,7 +2584,7 @@ public final class ProfileGiftsContext { extension ProfileGiftsContext.State.StarGift { init?(apiSavedStarGift: Api.SavedStarGift, peerId: EnginePeer.Id, transaction: Transaction) { switch apiSavedStarGift { - case let .savedStarGift(flags, fromId, date, apiGift, message, msgId, savedId, convertStars, upgradeStars, canExportDate, transferStars, canTransferAt, canResaleAt, collectionIds): + case let .savedStarGift(flags, fromId, date, apiGift, message, msgId, savedId, convertStars, upgradeStars, canExportDate, transferStars, canTransferAt, canResaleAt, collectionIds, prepaidUpgradeHash): guard let gift = StarGift(apiStarGift: apiGift) else { return nil } @@ -2457,6 +2631,8 @@ extension ProfileGiftsContext.State.StarGift { self.canTransferDate = canTransferAt self.canResaleDate = canResaleAt self.collectionIds = collectionIds + self.prepaidUpgradeHash = prepaidUpgradeHash + self.upgradeSeparate = (flags & (1 << 17)) != 0 } } } @@ -2498,9 +2674,9 @@ func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal mapToSignal { result -> Signal in if let result = result { switch result { - case let .uniqueStarGift(gift, users): + case let .uniqueStarGift(gift, chats, users): return account.postbox.transaction { transaction in - let parsedPeers = AccumulatedPeers(users: users) + let parsedPeers = AccumulatedPeers(chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers) guard case let .unique(uniqueGift) = StarGift(apiStarGift: gift) else { return nil @@ -2514,6 +2690,41 @@ func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal Signal { + return account.network.request(Api.functions.payments.getUniqueStarGiftValueInfo(slug: slug)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> StarGift.UniqueGift.ValueInfo? in + if let result { + switch result { + case let .uniqueStarGiftValueInfo(flags, currency, value, initialSaleDate, initialSaleStars, initialSalePrice, lastSaleDate, lastSalePrice, floorPrice, averagePrice, listedCount, fragmentListedCount, fragmentListedUrl): + let _ = listedCount + let _ = fragmentListedCount + return StarGift.UniqueGift.ValueInfo( + isLastSaleOnFragment: flags & (1 << 1) != 0, + valueIsAverage: flags & (1 << 6) != 0, + value: value, + currency: currency, + initialSaleDate: initialSaleDate, + initialSaleStars: initialSaleStars, + initialSalePrice: initialSalePrice, + lastSaleDate: lastSaleDate, + lastSalePrice: lastSalePrice, + floorPrice: floorPrice, + averagePrice: averagePrice, + listedCount: listedCount, + fragmentListedCount: fragmentListedCount, + fragmentListedUrl: fragmentListedUrl + ) + } + } else { + return nil + } + } +} + public enum StarGiftReference: Equatable, Hashable, Codable { enum CodingKeys: String, CodingKey { case type diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 0cca0de749..f2c19cb801 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -723,6 +723,9 @@ private extension StarsContext.State.Transaction { if (apiFlags & (1 << 24)) != 0 { flags.insert(.isPostsSearch) } + if (apiFlags & (1 << 25)) != 0 { + flags.insert(.isStarGiftPrepaidUpgrade) + } let media = extendedMedia.flatMap({ $0.compactMap { textMediaAndExpirationTimerFromApiMedia($0, PeerId(0)).media } }) ?? [] let _ = subscriptionPeriod @@ -778,6 +781,7 @@ public final class StarsContext { public static let isBusinessTransfer = Flags(rawValue: 1 << 8) public static let isStarGiftResale = Flags(rawValue: 1 << 9) public static let isPostsSearch = Flags(rawValue: 1 << 10) + public static let isStarGiftPrepaidUpgrade = Flags(rawValue: 1 << 11) } public enum Peer: Equatable { @@ -1622,10 +1626,10 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot receiptMessageId = id } } - case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale: + case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale, .starGiftPrepaidUpgrade: receiptMessageId = nil } - } else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, peerId, _, savedId, _, canTransferDate, canResaleDate) = action.action, case let .Id(messageId) = message.id { + } else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate) = action.action, case let .Id(messageId) = message.id { let reference: StarGiftReference if let peerId, let savedId { reference = .peer(peerId: peerId, id: savedId) @@ -1649,7 +1653,9 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot transferStars: transferStars, canTransferDate: canTransferDate, canResaleDate: canResaleDate, - collectionIds: nil + collectionIds: nil, + prepaidUpgradeHash: nil, + upgradeSeparate: false ) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index 4723234c4c..bfbd3d48b2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -141,10 +141,18 @@ public extension TelegramEngine { return _internal_starGiftUpgradePreview(account: self.account, giftId: giftId) } + public func checkCanSendStarGift(giftId: Int64) -> Signal { + return _internal_checkCanSendStarGift(account: self.account, giftId: giftId) + } + public func getUniqueStarGift(slug: String) -> Signal { return _internal_getUniqueStarGift(account: self.account, slug: slug) } + public func getUniqueStarGiftValueInfo(slug: String) -> Signal { + return _internal_getUniqueStarGiftValueInfo(account: self.account, slug: slug) + } + public func checkStarGiftWithdrawalAvailability(reference: StarGiftReference) -> Signal { return _internal_checkStarGiftWithdrawalAvailability(account: self.account, reference: reference) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ProfileTabs.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ProfileTabs.swift new file mode 100644 index 0000000000..bc314f5551 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ProfileTabs.swift @@ -0,0 +1,39 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +func _internal_setMainProfileTab(account: Account, peerId: PeerId, tab: TelegramProfileTab) -> Signal { + return account.postbox.loadedPeerWithId(peerId) + |> mapToSignal { peer in + return account.postbox.transaction { transaction -> Signal in + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedUserData { + return current.withUpdatedMainProfileTab(tab) + } else if let current = current as? CachedChannelData { + return current.withUpdatedMainProfileTab(tab) + } else { + return current + } + }) + if let inputChannel = apiInputChannel(peer) { + return account.network.request(Api.functions.channels.setMainProfileTab(channel: inputChannel, tab: tab.apiTab)) + |> `catch` { error in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } else { + return account.network.request(Api.functions.account.setMainProfileTab(tab: tab.apiTab)) + |> `catch` { error in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } + } |> switchToLatest + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SavedMusic.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SavedMusic.swift new file mode 100644 index 0000000000..62dc50b2d3 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SavedMusic.swift @@ -0,0 +1,464 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +public enum AddSavedMusicError { + case generic +} + +func revalidatedMusic(account: Account, file: FileMediaReference, signal: @escaping (CloudDocumentMediaResource) -> Signal) -> Signal { + guard let resource = file.media.resource as? CloudDocumentMediaResource else { + return .fail(MTRpcError(errorCode: 500, errorDescription: "Internal")) + } + return signal(resource) + |> `catch` { error -> Signal in + if error.errorDescription == "FILE_REFERENCE_EXPIRED" { + return revalidateMediaResourceReference(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: file.resourceReference(resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resource) + |> mapError { _ -> MTRpcError in + return MTRpcError(errorCode: 500, errorDescription: "Internal") + } + |> mapToSignal { result -> Signal in + guard let resource = result.updatedResource as? CloudDocumentMediaResource else { + return .fail(MTRpcError(errorCode: 500, errorDescription: "Internal")) + } + return signal(resource) + } + } else { + return .fail(error) + } + } +} + +public final class SavedMusicIdsList: Codable, Equatable { + public let items: [Int64] + + public init(items: [Int64]) { + self.items = items + } + + public static func ==(lhs: SavedMusicIdsList, rhs: SavedMusicIdsList) -> Bool { + if lhs === rhs { + return true + } + if lhs.items != rhs.items { + return false + } + return true + } +} + +func _internal_getSavedMusicById(postbox: Postbox, network: Network, peer: PeerReference, file: TelegramMediaFile) -> Signal { + let inputUser = peer.inputUser + guard let inputUser, let resource = file.resource as? CloudDocumentMediaResource else { + return .single(nil) + } + return network.request(Api.functions.users.getSavedMusicByID(id: inputUser, documents: [.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference))])) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> TelegramMediaFile? in + if let result { + switch result { + case let .savedMusic(_, documents): + if let file = documents.first.flatMap({ telegramMediaFileFromApiDocument($0, altDocuments: nil) }) { + return file + } + default: + break + } + } + return nil + } +} + +func _internal_savedMusicIds(postbox: Postbox) -> Signal?, NoError> { + let viewKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.savedMusicIds()])) + return postbox.combinedView(keys: [viewKey]) + |> map { views -> Set? in + guard let view = views.views[viewKey] as? PreferencesView else { + return nil + } + guard let value = view.values[PreferencesKeys.savedMusicIds()]?.get(SavedMusicIdsList.self) else { + return nil + } + return Set(value.items) + } +} + +func _internal_keepSavedMusicIdsUpdated(postbox: Postbox, network: Network, accountPeerId: EnginePeer.Id) -> Signal { + let updateSignal = _internal_savedMusicIds(postbox: postbox) + |> take(1) + |> mapToSignal { list -> Signal in + //TODO:release + return network.request(Api.functions.account.getSavedMusicIds(hash: 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result else { + return .complete() + } + return postbox.transaction { transaction in + switch result { + case let .savedMusicIds(ids): + let savedMusicIdsList = SavedMusicIdsList(items: ids) + transaction.setPreferencesEntry(key: PreferencesKeys.savedMusicIds(), value: PreferencesEntry(savedMusicIdsList)) + case .savedMusicIdsNotModified: + break + } + } + |> ignoreValues + } + } + + return updateSignal +} + +func managedSavedMusicIdsUpdates(postbox: Postbox, network: Network, accountPeerId: EnginePeer.Id) -> Signal { + let poll = _internal_keepSavedMusicIdsUpdated(postbox: postbox, network: network, accountPeerId: accountPeerId) + return (poll |> then(.complete() |> suspendAwareDelay(0.5 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart +} + +func _internal_addSavedMusic(account: Account, file: FileMediaReference, afterFile: FileMediaReference?) -> Signal { + return account.postbox.transaction { transaction in + if let cachedSavedMusic = transaction.retrieveItemCacheEntry(id: entryId(peerId: account.peerId))?.get(CachedProfileSavedMusic.self) { + var updatedFiles = cachedSavedMusic.files + var updatedCount = cachedSavedMusic.count + + if let fromIndex = updatedFiles.firstIndex(where: { $0.fileId == file.media.fileId }) { + let anchorIdxOpt: Int? = afterFile.flatMap { af in + updatedFiles.firstIndex(where: { $0.fileId == af.media.fileId }) + } + updatedFiles.remove(at: fromIndex) + let insertIndex: Int + if let anchorIndex = anchorIdxOpt { + if anchorIndex == fromIndex { + insertIndex = min(fromIndex + 1, updatedFiles.count) + } else { + let adjustedAnchor = anchorIndex > fromIndex ? (anchorIndex - 1) : anchorIndex + insertIndex = updatedFiles.index(after: adjustedAnchor) + } + } else if afterFile != nil { + insertIndex = 0 + } else { + insertIndex = 0 + } + + updatedFiles.insert(file.media, at: insertIndex) + } else { + if let afterFile, let anchor = updatedFiles.firstIndex(where: { $0.fileId == afterFile.media.fileId }) { + updatedFiles.insert(file.media, at: updatedFiles.index(after: anchor)) + } else if afterFile != nil { + updatedFiles.append(file.media) + } else { + updatedFiles.insert(file.media, at: 0) + } + updatedCount = updatedCount + 1 + } + + if let entry = CodableEntry(CachedProfileSavedMusic(files: updatedFiles, count: updatedCount)) { + transaction.putItemCacheEntry(id: entryId(peerId: account.peerId), entry: entry) + } + + if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.savedMusicIds())?.get(SavedMusicIdsList.self) { + var ids = Set(entry.items) + ids.insert(file.media.fileId.id) + let savedMusicIdsList = SavedMusicIdsList(items: Array(ids)) + transaction.setPreferencesEntry(key: PreferencesKeys.savedMusicIds(), value: PreferencesEntry(savedMusicIdsList)) + } + + if afterFile == nil { + transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, cachedData -> CachedPeerData? in + if let cachedData = cachedData as? CachedUserData { + var updatedData = cachedData + updatedData = updatedData.withUpdatedSavedMusic(file.media) + return updatedData + } else { + return cachedData + } + }) + } + } + return revalidatedMusic(account: account, file: file, signal: { resource in + var flags: Int32 = 0 + var afterId: Api.InputDocument? + if let afterFile, let resource = afterFile.media.resource as? CloudDocumentMediaResource { + flags = 1 << 1 + afterId = .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)) + } + return account.network.request(Api.functions.account.saveMusic(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), afterId: afterId)) + }) + |> mapError { _ -> AddSavedMusicError in + return .generic + } + |> mapToSignal { _ in + return .complete() + } + } + |> castError(AddSavedMusicError.self) + |> switchToLatest +} + +func _internal_removeSavedMusic(account: Account, file: FileMediaReference) -> Signal { + return account.postbox.transaction { transaction in + if let cachedSavedMusic = transaction.retrieveItemCacheEntry(id: entryId(peerId: account.peerId))?.get(CachedProfileSavedMusic.self) { + let updatedFiles = cachedSavedMusic.files.filter { $0.fileId != file.media.id } + let updatedCount = max(0, cachedSavedMusic.count - 1) + if let entry = CodableEntry(CachedProfileSavedMusic(files: updatedFiles, count: updatedCount)) { + transaction.putItemCacheEntry(id: entryId(peerId: account.peerId), entry: entry) + } + + if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.savedMusicIds())?.get(SavedMusicIdsList.self) { + var ids = Set(entry.items) + ids.remove(file.media.fileId.id) + let savedMusicIdsList = SavedMusicIdsList(items: Array(ids)) + transaction.setPreferencesEntry(key: PreferencesKeys.savedMusicIds(), value: PreferencesEntry(savedMusicIdsList)) + } + + if updatedCount == 0 { + transaction.updatePeerCachedData(peerIds: Set([account.peerId]), update: { _, cachedData -> CachedPeerData? in + if let cachedData = cachedData as? CachedUserData { + var updatedData = cachedData + updatedData = updatedData.withUpdatedSavedMusic(nil) + return updatedData + } else { + return cachedData + } + }) + } + } + let flags: Int32 = 1 << 0 + return revalidatedMusic(account: account, file: file, signal: { resource in + return account.network.request(Api.functions.account.saveMusic(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), afterId: nil)) + }) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ in + return .complete() + } + } + |> switchToLatest +} + +private final class CachedProfileSavedMusic: Codable { + enum CodingKeys: String, CodingKey { + case files + case count + } + + let files: [TelegramMediaFile] + let count: Int32 + + init(files: [TelegramMediaFile], count: Int32) { + self.files = files + self.count = count + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.files = try container.decode([TelegramMediaFile].self, forKey: .files) + self.count = try container.decode(Int32.self, forKey: .count) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.files, forKey: .files) + try container.encode(self.count, forKey: .count) + } +} + +private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId { + let cacheKey = ValueBoxKey(length: 8) + cacheKey.setInt64(0, value: peerId.toInt64()) + return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedProfileSavedMusic, key: cacheKey) +} + +public final class ProfileSavedMusicContext { + public struct State: Equatable { + public enum DataState: Equatable { + case loading + case ready(canLoadMore: Bool) + } + + public var files: [TelegramMediaFile] + public var count: Int32? + public var dataState: DataState + } + + private let queue: Queue = .mainQueue() + private let account: Account + public let peerId: EnginePeer.Id + + private let disposable = MetaDisposable() + private let cacheDisposable = MetaDisposable() + + private var files: [TelegramMediaFile] = [] + private var count: Int32? + private var dataState: ProfileSavedMusicContext.State.DataState = .ready(canLoadMore: true) + + private let stateValue = Promise() + public var state: Signal { + return self.stateValue.get() + } + + public init(account: Account, peerId: EnginePeer.Id) { + self.account = account + self.peerId = peerId + + self.loadMore() + } + + deinit { + self.disposable.dispose() + self.cacheDisposable.dispose() + } + + public func reload() { + self.files = [] + self.dataState = .ready(canLoadMore: true) + self.loadMore(reload: true) + } + + public func loadMore(reload: Bool = false) { + let peerId = self.peerId + let network = self.account.network + let postbox = self.account.postbox + let dataState = self.dataState + let offset = Int32(self.files.count) + + guard case .ready(true) = dataState else { + return + } + if self.files.isEmpty, !reload { + self.cacheDisposable.set((postbox.transaction { transaction -> CachedProfileSavedMusic? in + return transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileSavedMusic.self) + } |> deliverOn(self.queue)).start(next: { [weak self] cachedSavedMusic in + guard let self, let cachedSavedMusic else { + return + } + self.files = cachedSavedMusic.files + self.count = cachedSavedMusic.count + if case .loading = self.dataState { + self.pushState() + } + })) + } + + self.dataState = .loading + if !reload { + self.pushState() + } + + let signal = postbox.loadedPeerWithId(peerId) + |> castError(MTRpcError.self) + |> mapToSignal { peer -> Signal<([TelegramMediaFile], Int32), MTRpcError> in + guard let inputUser = apiInputUser(peer) else { + return .complete() + } + return network.request(Api.functions.users.getSavedMusic(id: inputUser, offset: offset, limit: 32, hash: 0)) + |> map { result -> ([TelegramMediaFile], Int32) in + switch result { + case let .savedMusic(count, documents): + return (documents.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: nil) }, count) + case let .savedMusicNotModified(count): + return ([], count) + } + } + } + + self.disposable.set((signal + |> deliverOn(self.queue)).start(next: { [weak self] files, count in + guard let self else { + return + } + if offset == 0 || reload { + self.files = files + self.cacheDisposable.set(self.account.postbox.transaction { transaction in + if let entry = CodableEntry(CachedProfileSavedMusic(files: files, count: count)) { + transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) + } + }.start()) + } else { + self.files.append(contentsOf: files) + } + + let updatedCount = max(Int32(self.files.count), count) + self.count = updatedCount + self.dataState = .ready(canLoadMore: count != 0 && updatedCount > self.files.count) + self.pushState() + })) + } + + public func addMusic(file: FileMediaReference, afterFile: FileMediaReference? = nil, apply: Bool = true) -> Signal { + var updatedFiles = self.files + + let fromIdx = updatedFiles.firstIndex { $0.fileId == file.media.fileId } + let anchorIdxOpt = afterFile.flatMap { af in + updatedFiles.firstIndex { $0.fileId == af.media.fileId } + } + + if let fromIdx = fromIdx { + updatedFiles.remove(at: fromIdx) + + let insertIdx: Int + if let anchorIdx = anchorIdxOpt { + if anchorIdx == fromIdx { + insertIdx = min(fromIdx + 1, updatedFiles.count) + } else { + let adjustedAnchor = anchorIdx > fromIdx ? (anchorIdx - 1) : anchorIdx + insertIdx = updatedFiles.index(after: adjustedAnchor) + } + } else if afterFile != nil { + insertIdx = updatedFiles.count + } else { + insertIdx = 0 + } + updatedFiles.insert(file.media, at: insertIdx) + } else { + if let anchorIdx = anchorIdxOpt { + updatedFiles.insert(file.media, at: updatedFiles.index(after: anchorIdx)) + } else if afterFile != nil { + updatedFiles.append(file.media) + } else { + updatedFiles.insert(file.media, at: 0) + } + if let count = self.count { + self.count = count + 1 + } + } + self.files = updatedFiles + self.pushState() + + if apply { + return _internal_addSavedMusic(account: self.account, file: file, afterFile: afterFile) + } else { + return .complete() + } + } + + public func removeMusic(file: FileMediaReference) -> Signal { + self.files.removeAll(where: { $0.fileId == file.media.id }) + if let count = self.count { + self.count = max(0, count - 1) + } + self.pushState() + + return _internal_removeSavedMusic(account: self.account, file: file) + } + + private func pushState() { + let state = State( + files: self.files, + count: self.count, + dataState: self.dataState + ) + self.stateValue.set(.single(state)) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 466ec24a13..048f67206b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -907,6 +907,22 @@ public extension TelegramEngine { return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init) } } + + public func setMainProfileTab(peerId: EnginePeer.Id, tab: TelegramProfileTab) -> Signal { + return _internal_setMainProfileTab(account: self.account, peerId: peerId, tab: tab) + } + + public func savedMusicIds() -> Signal?, NoError> { + return _internal_savedMusicIds(postbox: self.account.postbox) + } + + public func addSavedMusic(file: FileMediaReference, afterFile: FileMediaReference? = nil) -> Signal { + return _internal_addSavedMusic(account: self.account, file: file, afterFile: afterFile) + } + + public func removeSavedMusic(file: FileMediaReference) -> Signal { + return _internal_removeSavedMusic(account: self.account, file: file) + } public func getNextUnreadChannel(peerId: PeerId, chatListFilterId: Int32?, getFilterPredicate: @escaping (ChatListFilterData) -> ChatListFilterPredicate) -> Signal<(peer: EnginePeer, unreadCount: Int, location: NextUnreadChannelLocation)?, NoError> { let startTime = CFAbsoluteTimeGetCurrent() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 2c7d7b5498..4ecca44385 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -265,7 +265,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } switch fullUser { - case let .userFull(_, _, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .userFull(_, _, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: userFullNotifySettings)]) } @@ -277,7 +277,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee previous = CachedUserData() } switch fullUser { - case let .userFull(userFullFlags, userFullFlags2, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, groupAdminRights, channelAdminRights, userWallpaper, _, businessWorkHours, businessLocation, greetingMessage, awayMessage, businessIntro, birthday, personalChannelId, personalChannelMessage, starGiftsCount, starRefProgram, verification, sendPaidMessageStars, disallowedStarGifts, starsRating, starsMyPendingRating, starsMyPendingRatingDate): + case let .userFull(userFullFlags, userFullFlags2, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullChatTheme, _, groupAdminRights, channelAdminRights, userWallpaper, _, businessWorkHours, businessLocation, greetingMessage, awayMessage, businessIntro, birthday, personalChannelId, personalChannelMessage, starGiftsCount, starRefProgram, verification, sendPaidMessageStars, disallowedStarGifts, starsRating, starsMyPendingRating, starsMyPendingRatingDate, mainTab, savedMusic): let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:)) let isBlocked = (userFullFlags & (1 << 0)) != 0 let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0 @@ -422,7 +422,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let botGroupAdminRights = groupAdminRights.flatMap { TelegramChatAdminRights(apiAdminRights: $0) } let botChannelAdminRights = channelAdminRights.flatMap { TelegramChatAdminRights(apiAdminRights: $0) } - let parsedStarRating = starsRating.flatMap(TelegramStarRating.init(apiRating:)) + let mappedStarRating = starsRating.flatMap(TelegramStarRating.init(apiRating:)) var pendingRating: TelegramStarPendingRating? if let starsMyPendingRating, let starsMyPendingRatingDate { pendingRating = TelegramStarPendingRating( @@ -431,6 +431,11 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee ) } + let mappedMainProfileTab = mainTab.flatMap { TelegramProfileTab(apiTab: $0) } + let mappedSavedMusic = savedMusic.flatMap { telegramMediaFileFromApiDocument($0, altDocuments: nil) } + + let mappedChatTheme: ChatTheme? = userFullChatTheme.flatMap { ChatTheme(apiChatTheme: $0) } + return previous.withUpdatedAbout(userFullAbout) .withUpdatedBotInfo(botInfo) .withUpdatedEditableBotInfo(editableBotInfo) @@ -444,7 +449,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedPinnedMessageId(pinnedMessageId) .withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedAutoremoveTimeout(autoremoveTimeout) - .withUpdatedThemeEmoticon(userFullThemeEmoticon) + .withUpdatedChatTheme(mappedChatTheme) .withUpdatedPhoto(.known(photo)) .withUpdatedPersonalPhoto(.known(personalPhoto)) .withUpdatedFallbackPhoto(.known(fallbackPhoto)) @@ -467,8 +472,10 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedDisallowedGifts(disallowedGifts) .withUpdatedBotGroupAdminRights(botGroupAdminRights) .withUpdatedBotChannelAdminRights(botChannelAdminRights) - .withUpdatedStarRating(parsedStarRating) + .withUpdatedStarRating(mappedStarRating) .withUpdatedPendingStarRating(pendingRating) + .withUpdatedMainProfileTab(mappedMainProfileTab) + .withUpdatedSavedMusic(mappedSavedMusic) } }) } @@ -575,6 +582,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let mappedReactionSettings = PeerReactionSettings(allowedReactions: mappedAllowedReactions, maxReactionCount: reactionsLimit, starsAllowed: nil) + let mappedChatTheme: ChatTheme? = chatFullThemeEmoticon.flatMap { .emoticon($0) } + return previous.withUpdatedParticipants(participants) .withUpdatedExportedInvitation(exportedInvitation) .withUpdatedBotInfos(botInfos) @@ -586,7 +595,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedPhoto(photo) .withUpdatedActiveCall(updatedActiveCall) .withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId) - .withUpdatedThemeEmoticon(chatFullThemeEmoticon) + .withUpdatedChatTheme(mappedChatTheme) .withUpdatedInviteRequestsPending(chatFullRequestsPending) .withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedReactionSettings(.known(mappedReactionSettings)) @@ -627,14 +636,14 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch result { case let .chatFull(fullChat, chats, users): switch fullChat { - case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .chatFull: break } switch fullChat { - case let .channelFull(flags, flags2, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, requestsPending, _, defaultSendAs, allowedReactions, reactionsLimit, _, wallpaper, appliedBoosts, boostsUnrestrict, emojiSet, verification, starGiftsCount, sendPaidMessageStars): + case let .channelFull(flags, flags2, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, requestsPending, _, defaultSendAs, allowedReactions, reactionsLimit, _, wallpaper, appliedBoosts, boostsUnrestrict, emojiSet, verification, starGiftsCount, sendPaidMessageStars, mainTab): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { channelFlags.insert(.canDisplayParticipants) @@ -844,8 +853,12 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let verification = verification.flatMap { PeerVerification(apiBotVerification: $0) } - let parsedSendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } - + let mappedSendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } + + let mappedMainProfileTab = mainTab.flatMap { TelegramProfileTab(apiTab: $0) } + + let mappedChatTheme: ChatTheme? = themeEmoticon.flatMap { .emoticon($0) } + return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) .withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount)) @@ -868,7 +881,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId) .withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedPendingSuggestions(pendingSuggestions ?? []) - .withUpdatedThemeEmoticon(themeEmoticon) + .withUpdatedChatTheme(mappedChatTheme) .withUpdatedInviteRequestsPending(requestsPending) .withUpdatedSendAsPeerId(sendAsPeerId) .withUpdatedReactionSettings(.known(mappedReactionSettings)) @@ -880,7 +893,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedEmojiPack(emojiPack) .withUpdatedVerification(verification) .withUpdatedStarGiftsCount(starGiftsCount) - .withUpdatedSendPaidMessageStars(parsedSendPaidMessageStars) + .withUpdatedSendPaidMessageStars(mappedSendPaidMessageStars) + .withUpdatedMainProfileTab(mappedMainProfileTab) }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index 9423e2c582..d0758efd41 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -362,11 +362,10 @@ public extension TelegramEngine { |> take(1) |> mapToSignal { datacenterId -> Signal in let resource = AlbumCoverResource(datacenterId: Int(datacenterId), file: file, title: title, performer: performer, isThumbnail: isThumbnail) - return multipartFetch(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, mediaReferenceRevalidationContext: self.account.mediaReferenceRevalidationContext, networkStatsContext: self.account.networkStatsContext, resource: resource, datacenterId: Int(datacenterId), size: nil, intervals: .single([(0 ..< Int64.max, .default)]), parameters: MediaResourceFetchParameters( tag: nil, info: TelegramCloudMediaResourceFetchInfo( - reference: MediaResourceReference.standalone(resource: resource), + reference: file?.resourceReference(resource) ?? MediaResourceReference.standalone(resource: resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false ), diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift index 49798b8a9f..d946ce72ca 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift @@ -31,6 +31,167 @@ public final class ChatThemes: Codable, Equatable { } } +public enum ChatTheme: PostboxCoding, Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case type + case emoticon + case gift + case themeSettings + } + + case emoticon(String) + case gift(StarGift, [TelegramThemeSettings]) + + public init(decoder: PostboxDecoder) { + let type = decoder.decodeInt32ForKey(CodingKeys.type.rawValue, orElse: 0) + switch type { + case 0: + self = .emoticon(decoder.decodeStringForKey(CodingKeys.emoticon.rawValue, orElse: "")) + case 1: + self = .gift(decoder.decodeObjectForKey(CodingKeys.gift.rawValue, decoder: { StarGift(decoder: $0) }) as! StarGift, decoder.decodeCodable([TelegramThemeSettings].self, forKey: CodingKeys.themeSettings.rawValue) ?? []) + default: + assertionFailure() + self = .emoticon("") + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let type = try container.decode(Int32.self, forKey: .type) + switch type { + case 0: + self = .emoticon(try container.decode(String.self, forKey: .emoticon)) + case 1: + self = .gift(try container.decode(StarGift.self, forKey: .gift), try container.decode([TelegramThemeSettings].self, forKey: .themeSettings)) + default: + assertionFailure() + self = .emoticon("") + } + } + + public func encode(_ encoder: PostboxEncoder) { + switch self { + case let .emoticon(emoticon): + encoder.encodeInt32(0, forKey: CodingKeys.type.rawValue) + encoder.encodeString(emoticon, forKey: CodingKeys.emoticon.rawValue) + case let .gift(gift, themeSettings): + encoder.encodeInt32(1, forKey: CodingKeys.type.rawValue) + encoder.encodeObject(gift, forKey: CodingKeys.gift.rawValue) + encoder.encodeCodable(themeSettings, forKey: CodingKeys.themeSettings.rawValue) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + switch self { + case let .emoticon(emoticon): + try container.encode(0 as Int32, forKey: .type) + try container.encode(emoticon, forKey: .emoticon) + case let .gift(gift, themeSettings): + try container.encode(1 as Int32, forKey: .type) + try container.encode(gift, forKey: .gift) + try container.encode(themeSettings, forKey: .themeSettings) + } + } + + public static func ==(lhs: ChatTheme, rhs: ChatTheme) -> Bool { + switch lhs { + case let .emoticon(emoticon): + if case .emoticon(emoticon) = rhs { + return true + } else { + return false + } + case let .gift(lhsGift, lhsThemeSettings): + if case let .gift(rhsGift, rhsThemeSettings) = rhs { + switch lhsGift { + case let .generic(lhsGeneric): + switch rhsGift { + case let .generic(rhsGeneric): + return lhsGeneric == rhsGeneric && lhsThemeSettings == rhsThemeSettings + default: + return false + } + case let .unique(lhsUnique): + switch rhsGift { + case let .unique(rhsUnique): + return lhsUnique.slug == rhsUnique.slug && lhsThemeSettings == rhsThemeSettings + default: + return false + } + } + } else { + return false + } + } + } + + public var isEmpty: Bool { + if case .emoticon("") = self { + return true + } else { + return false + } + } + + public var id: String { + switch self { + case let .emoticon(emoticon): + return emoticon.strippedEmoji + case let .gift(gift, _): + if case let .unique(uniqueGift) = gift { + return uniqueGift.slug + } else { + fatalError() + } + } + } + + public func withThemePeerId(_ themePeerId: EnginePeer.Id?) -> ChatTheme { + switch self { + case .emoticon: + return self + case let .gift(gift, themeSettings): + switch gift { + case let .unique(uniqueGift): + return .gift(.unique(uniqueGift.withThemePeerId(themePeerId)), themeSettings) + case .generic: + return self + } + } + } +} + +extension ChatTheme { + init?(apiChatTheme: Api.ChatTheme) { + switch apiChatTheme { + case let .chatTheme(emoticon): + self = .emoticon(emoticon) + case let .chatThemeUniqueGift(gift, themeSettings): + guard let gift = StarGift(apiStarGift: gift) else { + return nil + } + self = .gift(gift, themeSettings.compactMap { TelegramThemeSettings(apiThemeSettings: $0) }) + } + } + + var apiChatTheme: Api.InputChatTheme { + switch self { + case let .emoticon(emoticon): + return .inputChatTheme(emoticon: emoticon) + case let .gift(gift, _): + switch gift { + case let .unique(uniqueGift): + return .inputChatThemeUniqueGift(slug: uniqueGift.slug) + default: + fatalError() + } + } + } +} + func _internal_getChatThemes(accountManager: AccountManager, network: Network, forceUpdate: Bool = false, onlyCached: Bool = false) -> Signal<[TelegramTheme], NoError> { let fetch: ([TelegramTheme]?, Int64?) -> Signal<[TelegramTheme], NoError> = { current, hash in return network.request(Api.functions.account.getChatThemes(hash: hash ?? 0)) @@ -81,27 +242,48 @@ func _internal_getChatThemes(accountManager: AccountManager Signal { +func _internal_setChatTheme(account: Account, peerId: PeerId, chatTheme: ChatTheme?) -> Signal { return account.postbox.loadedPeerWithId(peerId) |> mapToSignal { peer in guard let inputPeer = apiInputPeer(peer) else { return .complete() } - return account.postbox.transaction { transaction -> Signal in + var chatTheme = chatTheme + if case let .gift(gift, _) = chatTheme, case let .unique(uniqueGift) = gift, let previousThemePeerId = uniqueGift.themePeerId { + transaction.updatePeerCachedData(peerIds: Set([previousThemePeerId]), update: { _, current in + if let current = current as? CachedUserData { + return current.withUpdatedChatTheme(nil) + } else if let current = current as? CachedGroupData { + return current.withUpdatedChatTheme(nil) + } else if let current = current as? CachedChannelData { + return current.withUpdatedChatTheme(nil) + } else { + return current + } + }) + } + chatTheme = chatTheme?.withThemePeerId(peerId) + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if let current = current as? CachedUserData { - return current.withUpdatedThemeEmoticon(emoticon) + return current.withUpdatedChatTheme(chatTheme) } else if let current = current as? CachedGroupData { - return current.withUpdatedThemeEmoticon(emoticon) + return current.withUpdatedChatTheme(chatTheme) } else if let current = current as? CachedChannelData { - return current.withUpdatedThemeEmoticon(emoticon) + return current.withUpdatedChatTheme(chatTheme) } else { return current } }) - return account.network.request(Api.functions.messages.setChatTheme(peer: inputPeer, emoticon: emoticon ?? "")) + let inputTheme: Api.InputChatTheme + if let chatTheme { + inputTheme = chatTheme.apiChatTheme + } else { + inputTheme = .inputChatThemeEmpty + } + return account.network.request(Api.functions.messages.setChatTheme(peer: inputPeer, theme: inputTheme)) |> `catch` { error in return .complete() } @@ -266,3 +448,159 @@ func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId, } } } + +private final class CachedUniqueGiftChatThemes: Codable { + enum CodingKeys: String, CodingKey { + case themes + } + + let themes: [ChatTheme] + + init(themes: [ChatTheme]) { + self.themes = themes + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.themes = try container.decode([ChatTheme].self, forKey: .themes) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.themes, forKey: .themes) + } +} + +private func entryId() -> ItemCacheEntryId { + let cacheKey = ValueBoxKey(length: 8) + cacheKey.setInt64(0, value: 0) + return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedChatThemes, key: cacheKey) +} + +public final class UniqueGiftChatThemesContext { + public struct State: Equatable { + public enum DataState: Equatable { + case loading + case ready(canLoadMore: Bool) + } + + public var themes: [ChatTheme] + public var dataState: DataState + } + + private let queue: Queue = .mainQueue() + private let account: Account + + private let disposable = MetaDisposable() + private let cacheDisposable = MetaDisposable() + + private var themes: [ChatTheme] = [] + private var nextOffset: Int32 = 0 + private var dataState: UniqueGiftChatThemesContext.State.DataState = .ready(canLoadMore: true) + + private let stateValue = Promise() + public var state: Signal { + return self.stateValue.get() + } + + public init(account: Account) { + self.account = account + + self.loadMore() + } + + deinit { + self.disposable.dispose() + self.cacheDisposable.dispose() + } + + public func reload() { + self.themes = [] + self.nextOffset = 0 + self.dataState = .ready(canLoadMore: true) + self.loadMore(reload: true) + } + + public func loadMore(reload: Bool = false) { + let network = self.account.network + let postbox = self.account.postbox + let accountPeerId = self.account.peerId + let dataState = self.dataState + let offset = self.nextOffset + + guard case .ready(true) = dataState else { + return + } + if self.themes.isEmpty, !reload { + self.cacheDisposable.set((postbox.transaction { transaction -> CachedUniqueGiftChatThemes? in + return transaction.retrieveItemCacheEntry(id: entryId())?.get(CachedUniqueGiftChatThemes.self) + } |> deliverOn(self.queue)).start(next: { [weak self] cachedUniqueGiftChatThemes in + guard let self, let cachedUniqueGiftChatThemes else { + return + } + self.themes = cachedUniqueGiftChatThemes.themes + if case .loading = self.dataState { + self.pushState() + } + })) + } + + self.dataState = .loading + if !reload { + self.pushState() + } + + let signal = network.request(Api.functions.account.getUniqueGiftChatThemes(offset: offset, limit: 50, hash: 0)) + |> map(Optional.init) + |> `catch` { error in + return .single(nil) + } + |> mapToSignal { result -> Signal<([ChatTheme], Int32?), NoError> in + guard let result else { + return .single(([], nil)) + } + return postbox.transaction { transaction -> ([ChatTheme], Int32?) in + switch result { + case let .chatThemes(_, _, themes, chats, users, nextOffset): + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + return (themes.compactMap { ChatTheme(apiChatTheme: $0) }, nextOffset) + case .chatThemesNotModified: + return ([], nil) + } + } + } + + self.disposable.set((signal + |> deliverOn(self.queue)).start(next: { [weak self] themes, nextOffset in + guard let self else { + return + } + if offset == 0 || reload { + self.themes = themes + self.cacheDisposable.set(self.account.postbox.transaction { transaction in + if let entry = CodableEntry(CachedUniqueGiftChatThemes(themes: themes)) { + transaction.putItemCacheEntry(id: entryId(), entry: entry) + } + }.start()) + } else { + self.themes.append(contentsOf: themes) + } + if let nextOffset { + self.nextOffset = nextOffset + } + self.dataState = .ready(canLoadMore: nextOffset != nil) + self.pushState() + })) + } + + private func pushState() { + let state = State( + themes: self.themes, + dataState: self.dataState + ) + self.stateValue.set(.single(state)) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift index 8e8ea3b097..b6b890c3a7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift @@ -13,8 +13,8 @@ public extension TelegramEngine { return _internal_getChatThemes(accountManager: accountManager, network: self.account.network, forceUpdate: forceUpdate, onlyCached: onlyCached) } - public func setChatTheme(peerId: PeerId, emoticon: String?) -> Signal { - return _internal_setChatTheme(account: self.account, peerId: peerId, emoticon: emoticon) + public func setChatTheme(peerId: PeerId, chatTheme: ChatTheme?) -> Signal { + return _internal_setChatTheme(account: self.account, peerId: peerId, chatTheme: chatTheme) } public func setChatWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?, forBoth: Bool) -> Signal { diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 393234c0a0..85d8692595 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -246,6 +246,18 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ } } } + + if let channel = peer as? TelegramChannel, let linkedBotId = channel.linkedBotId { + if let channelPeer = peers[linkedBotId] { + messagePeers[linkedBotId] = channelPeer + } + + if let threadId = message.threadId { + if let threadPeer = peers[PeerId(threadId)] { + messagePeers[threadPeer.id] = threadPeer + } + } + } } for media in message.media { diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index 02cfe99123..5e3ecc60ea 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -467,6 +467,8 @@ public func peerViewMonoforumMainPeer(_ view: PeerView) -> Peer? { if let peer = peerViewMainPeer(view) { if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { return view.peers[linkedMonoforumId] + } else if let channel = peer as? TelegramChannel, let linkedBotId = channel.linkedBotId { + return view.peers[linkedBotId] } else { return nil } @@ -507,8 +509,14 @@ public extension RenderedPeer { } var chatOrMonoforumMainPeer: Peer? { - if let channel = self.peer as? TelegramChannel, channel.flags.contains(.isMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { - return self.peers[linkedMonoforumId] + if let channel = self.peer as? TelegramChannel { + if channel.flags.contains(.isMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { + return self.peers[linkedMonoforumId] + } else if let linkedBotId = channel.linkedBotId { + return self.peers[linkedBotId] + } else { + return self.chatMainPeer + } } else { return self.chatMainPeer } diff --git a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift index 6362c49c48..27e0300312 100644 --- a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift @@ -43,7 +43,7 @@ func _internal_storeMessageFromSearch(transaction: Transaction, message: Message } } - let storeMessage = StoreMessage(id: .Id(message.id), globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media) + let storeMessage = StoreMessage(id: .Id(message.id), customStableId: nil, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media) let _ = transaction.addMessages([storeMessage], location: .Random) } diff --git a/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift b/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift index a8e92b7032..5a16f2cce0 100644 --- a/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift +++ b/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift @@ -23,7 +23,7 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) { 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) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } } @@ -113,7 +113,7 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadKe channelThreadMessageId = attribute.messageId } } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) if let channelThreadMessageId = channelThreadMessageId { diff --git a/submodules/TelegramCore/Sources/WebpagePreview.swift b/submodules/TelegramCore/Sources/WebpagePreview.swift index 7109d300d1..dbc8440822 100644 --- a/submodules/TelegramCore/Sources/WebpagePreview.swift +++ b/submodules/TelegramCore/Sources/WebpagePreview.swift @@ -157,7 +157,7 @@ public func webpagePreviewWithProgress(account: Account, urls: [String], webpage return account.network.requestWithAdditionalInfo(Api.functions.messages.getWebPagePreview(flags: 0, message: urls.joined(separator: " "), entities: nil), info: .progress) |> `catch` { _ -> Signal, NoError> in - return .single(.result(.webPagePreview(media: .messageMediaEmpty, users: []))) + return .single(.result(.webPagePreview(media: .messageMediaEmpty, chats: [], users: []))) } |> mapToSignal { result -> Signal in switch result { @@ -170,17 +170,17 @@ public func webpagePreviewWithProgress(account: Account, urls: [String], webpage return .complete() } case let .result(result): - if case let .webPagePreview(result, _) = result, let preCachedResources = result.preCachedResources { + if case let .webPagePreview(result, _, _) = result, let preCachedResources = result.preCachedResources { for (resource, data) in preCachedResources { account.postbox.mediaBox.storeResourceData(resource.id, data: data) } } switch result { - case let .webPagePreview(media, users): + case let .webPagePreview(media, chats, users): switch media { case let .messageMediaWebPage(_, webpage): return account.postbox.transaction { transaction -> Signal in - let peers = AccumulatedPeers(users: users) + let peers = AccumulatedPeers(chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: peers) if let media = telegramMediaWebpageFromApiWebpage(webpage), let url = media.content.url { diff --git a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift index 0698eab825..3f0398e984 100644 --- a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift @@ -47,13 +47,36 @@ public func makePresentationTheme(cloudTheme: TelegramTheme, dark: Bool = false) } else { settings = nil } - guard let settings = settings else { + guard let settings else { return nil } let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: .cloud(PresentationCloudTheme(theme: cloudTheme, resolvedWallpaper: nil, creatorAccountId: nil)), serviceBackgroundColor: nil, preview: false) return customizePresentationTheme(defaultTheme, editing: true, accentColor: UIColor(argb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(argb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper) } +public func makePresentationTheme(chatTheme: ChatTheme, dark: Bool = false) -> PresentationTheme? { + guard case let .gift(_, themeSettings) = chatTheme else { + return nil + } + let settings: TelegramThemeSettings? + if let exactSettings = themeSettings.first(where: { dark ? ($0.baseTheme == .night || $0.baseTheme == .tinted) : ($0.baseTheme == .classic || $0.baseTheme == .day) }) { + settings = exactSettings + } else if let firstSettings = themeSettings.first { + settings = firstSettings + } else { + settings = nil + } + guard let settings else { + return nil + } + let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), serviceBackgroundColor: nil, preview: false) + let theme = customizePresentationTheme(defaultTheme, editing: false, accentColor: UIColor(rgb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(rgb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper) + if case let .gift(starGiftValue, _) = chatTheme { + theme.starGift = starGiftValue + } + return theme +} + public func makePresentationTheme(cloudTheme: TelegramTheme, baseTheme: TelegramBaseTheme? = nil) -> PresentationTheme? { let settings: TelegramThemeSettings? if let exactSettings = cloudTheme.settings?.first(where: { $0.baseTheme == baseTheme }) { diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index 2f71522c77..f7cdcc3d64 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -1574,6 +1574,7 @@ public final class PresentationTheme: Equatable { public let chart: PresentationThemeChart public let preview: Bool public var forceSync: Bool = false + public var starGift: StarGift? public let resourceCache: PresentationsResourceCache = PresentationsResourceCache() diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 24ab1b6267..b5f32e6612 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -128,6 +128,7 @@ public enum PresentationResourceKey: Int32 { case chatListGeneralTopicIcon case chatListGeneralTopicTemplateIcon + case chatListNewTopicTemplateIcon case chatListGeneralTopicSmallIcon case searchAdIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index 9536782077..b8b8335927 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -137,7 +137,7 @@ public struct PresentationResourcesChat { public static func chatBubbleArrowFreeImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatBubbleArrowFreeImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/HeaderArrow"), color: UIColor(white: 1.0, alpha: 0.3)) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/HeaderArrow"), color: UIColor(white: 1.0, alpha: 0.4)) }) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift index a152ad6a35..bbffb17577 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift @@ -451,6 +451,23 @@ public struct PresentationResourcesChatList { }) } + public static func newTopicTemplateIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.chatListNewTopicTemplateIcon.rawValue, { theme in + guard let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: .white) else { + return nil + } + let size = CGSize(width: 24.0, height: 24.0) + return generateImage(size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + defer { + UIGraphicsPopContext() + } + image.draw(in: CGRect(origin: CGPoint(), size: size)) + })?.withRenderingMode(.alwaysTemplate) + }) + } + public static func statusAutoremoveIcon(_ theme: PresentationTheme, isActive: Bool) -> UIImage? { return theme.image(PresentationResourceParameterKey.statusAutoremoveIcon(isActive: isActive), { theme in return generateTintedImage(image: UIImage(bundleImageName: isActive ? "Chat List/StatusIconAutoremoveOn" : "Chat List/StatusIconAutoremoveOff"), color: isActive ? theme.list.itemAccentColor : theme.list.itemSecondaryTextColor) diff --git a/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift b/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift index 3733889b57..2b81e47ed7 100644 --- a/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift +++ b/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift @@ -156,6 +156,23 @@ public func currencyToFractionalAmount(value: Int64, currency: String) -> Double return Double(value) / factor } +private func formatIntegerWithThousandsSeparator(_ number: Int64, separator: String) -> String { + if number == 0 { + return "0" + } + let numberString = String(number) + var result = "" + let digits = Array(numberString) + for (index, digit) in digits.enumerated() { + let remainingDigits = digits.count - index + if remainingDigits % 3 == 0 && index > 0 { + result.append(separator) + } + result.append(digit) + } + return result +} + public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String { if let entry = currencyFormatterEntries[currency] ?? currencyFormatterEntries["USD"] { var result = "" @@ -177,12 +194,13 @@ public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String { fractional.append(Character(scalar)) } } - result.append("\(integerPart)") + let integerString = formatIntegerWithThousandsSeparator(integerPart, separator: entry.thousandsSeparator) + result.append(integerString) if !fractional.isEmpty { result.append(entry.decimalSeparator) - } - for i in 0 ..< fractional.count { - result.append(fractional[fractional.count - i - 1]) + for i in 0 ..< fractional.count { + result.append(fractional[fractional.count - i - 1]) + } } if !entry.symbolOnLeft { if entry.spaceBetweenAmountAndSymbol { @@ -190,7 +208,6 @@ public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String { } result.append(entry.symbol) } - return result } else { assertionFailure() diff --git a/submodules/TelegramStringFormatting/Sources/GiftFormat.swift b/submodules/TelegramStringFormatting/Sources/GiftFormat.swift new file mode 100644 index 0000000000..994a988e11 --- /dev/null +++ b/submodules/TelegramStringFormatting/Sources/GiftFormat.swift @@ -0,0 +1,10 @@ +import Foundation +import TelegramPresentationData + +public func formatCollectibleNumber(_ number: Int32, dateTimeFormat: PresentationDateTimeFormat) -> String { + if number > 9999 { + return presentationStringsFormattedNumber(number, dateTimeFormat.groupingSeparator) + } else { + return "\(number)" + } +} diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index c5a341fc21..a13b4239ea 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -767,8 +767,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) - case let .setChatTheme(emoji): - if emoji.isEmpty { + case let .setChatTheme(chatTheme): + if chatTheme.isEmpty { if message.author?.id.namespace == Namespaces.Peer.CloudChannel { attributedString = NSAttributedString(string: strings.Notification_ChannelDisabledTheme, font: titleFont, textColor: primaryTextColor) } else if message.author?.id == accountPeerId { @@ -779,13 +779,47 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) } } else { + var emoji = "" + var additionalAttributes: [String: Any] = [:] + var giftTitle: String? + switch chatTheme { + case let .emoticon(emoticon): + emoji = emoticon + case let .gift(starGift, _): + var file: TelegramMediaFile? + + if case let .unique(uniqueGift) = starGift { + giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: dateTimeFormat))" + for attribute in uniqueGift.attributes { + if case let .model(_, fileValue, _) = attribute { + file = fileValue + break + } + } + } + if let file { + additionalAttributes[ChatTextInputAttributes.customEmoji.rawValue] = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file, custom: nil) + } + } + let emojiAttributes = MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: additionalAttributes) if message.author?.id.namespace == Namespaces.Peer.CloudChannel { attributedString = NSAttributedString(string: strings.Notification_ChannelChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor) } else if message.author?.id == accountPeerId { - attributedString = NSAttributedString(string: strings.Notification_YouChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor) + if let giftTitle { + let resultTitleString = strings.Notification_YouChangedThemeGift(giftTitle) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [:]) + } else { + let resultTitleString = strings.Notification_YouChangedTheme(emoji) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: emojiAttributes]) + } } else { - let resultTitleString = strings.Notification_ChangedTheme(compactAuthorName, emoji) - attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + if let giftTitle { + let resultTitleString = strings.Notification_ChangedThemeGift(compactAuthorName, giftTitle) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes]) + } else { + let resultTitleString = strings.Notification_ChangedTheme(compactAuthorName, emoji) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: emojiAttributes]) + } } } case let .webViewData(text): @@ -796,7 +830,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } else { let price: String if currency == "XTR" { - price = "\(amount) Stars" + price = strings.Notification_PremiumGift_Stars(Int32(clamping: amount)) } else { price = formatCurrencyAmount(amount, currency: currency) } @@ -1133,7 +1167,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = mutableString case .prizeStars: attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor) - case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, _, peerId, senderId, _): + case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, isPrepaidUpgrade, _, peerId, senderId, _, _, _, upgradeSeparate): if !forAdditionalServiceMessage { if let text { let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage())) @@ -1143,7 +1177,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } } else if case let .generic(gift) = gift { var finalPrice = gift.price - if let upgradeStars { + if let upgradeStars, !upgradeSeparate { finalPrice += upgradeStars } let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: finalPrice)) @@ -1158,29 +1192,66 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } else if message.id.peerId == accountPeerId { attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Self_Bought(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) } else if message.author?.id == accountPeerId { - attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) - } else if let peerId { - peerIds = [(1, peerId)] - var peerName = "" - if let name = message.peers[peerId].flatMap(EnginePeer.init)?.compactDisplayTitle { - peerName = name - } - if let senderId { - peerIds.insert((0, senderId), at: 0) - if let name = message.peers[senderId].flatMap(EnginePeer.init)?.compactDisplayTitle { - authorName = name + if isPrepaidUpgrade, let peerId { + peerIds = [(1, peerId)] + var peerName = "" + if let name = message.peers[peerId].flatMap(EnginePeer.init)?.compactDisplayTitle { + peerName = name } + let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: upgradeStars ?? 0)) + if let senderId, senderId != accountPeerId { + if senderId == peerId { + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) + attributes[0] = boldAttributes + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentUpgradeYouUnknown(starsPrice, peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } else { + var otherPeerName = "" + if let name = message.peers[senderId].flatMap(EnginePeer.init)?.compactDisplayTitle { + otherPeerName = name + } + peerIds = [(1, peerId), (2, senderId)] + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) + attributes[0] = boldAttributes + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentUpgradeYouOther(starsPrice, peerName, otherPeerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } + } else { + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) + attributes[0] = boldAttributes + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentUpgradeYou(starsPrice, peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } + } else { + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + } + } else if let peerId { + if isPrepaidUpgrade { + peerIds = [(1, peerId)] + let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: upgradeStars ?? 0)) + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) + attributes[0] = boldAttributes + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentUpgrade(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } else { + peerIds = [(1, peerId)] + var peerName = "" + if let name = message.peers[peerId].flatMap(EnginePeer.init)?.compactDisplayTitle { + peerName = name + } + if let senderId { + peerIds.insert((0, senderId), at: 0) + if let name = message.peers[senderId].flatMap(EnginePeer.init)?.compactDisplayTitle { + authorName = name + } + } + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) + attributes[2] = boldAttributes + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Channel_Sent(authorName, peerName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes) } - var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) - attributes[2] = boldAttributes - attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Channel_Sent(authorName, peerName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes) } else { var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) attributes[1] = boldAttributes attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes) } } - case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, peerId, senderId, _, resaleStars, _, _): + case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _): if case let .unique(gift) = gift { if !forAdditionalServiceMessage && !"".isEmpty { attributedString = NSAttributedString(string: "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))", font: titleFont, textColor: primaryTextColor) @@ -1194,10 +1265,18 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = NSAttributedString(string: strings.Notification_StarsGift_UpgradeSelf, font: titleFont, textColor: primaryTextColor) } else if message.author?.id == accountPeerId { let attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) - attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_UpgradeYou(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + if isPrepaidUpgrade { + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_UpgradePrepaidYou(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } else { + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_UpgradeYou(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } } else { let attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) - attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Upgrade(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + if isPrepaidUpgrade { + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_UpgradePrepaid(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } else { + attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Upgrade(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) + } } } else { if message.id.peerId.isTelegramNotifications && senderId == nil { diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 7d4d317c64..3003487c92 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -353,6 +353,7 @@ swift_library( "//submodules/TelegramUI/Components/AudioWaveformNode", "//submodules/TelegramUI/Components/Chat/ChatBotInfoItem", "//submodules/TelegramUI/Components/Chat/ChatUserInfoItem", + "//submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem", "//submodules/TelegramUI/Components/Chat/ChatInputPanelNode", "//submodules/TelegramUI/Components/Chat/ChatBotStartInputPanelNode", "//submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode", @@ -483,6 +484,8 @@ swift_library( "//submodules/TelegramUI/Components/SuggestedPostApproveAlert", "//submodules/TelegramUI/Components/Stars/BalanceNeededScreen", "//submodules/TelegramUI/Components/FaceScanScreen", + "//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist", + "//submodules/TelegramUI/Components/ChatThemeScreen", "//submodules/ContactsHelper", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, diff --git a/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift b/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift index 0967a73fbe..c5d1ea1d80 100644 --- a/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift +++ b/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift @@ -4,6 +4,23 @@ import Display import ComponentFlow import TelegramPresentationData +extension ComponentTransition { + func animateBlur(layer: CALayer, from: CGFloat, to: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { + if let blurFilter = CALayer.blur() { + blurFilter.setValue(to as NSNumber, forKey: "inputRadius") + layer.filters = [blurFilter] + layer.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.3, delay: delay, removeOnCompletion: removeOnCompletion, completion: { [weak layer] _ in + guard let layer else { + return + } + if to == 0.0 && removeOnCompletion { + layer.filters = nil + } + }) + } + } +} + public final class AnimatedTextComponent: Component { public struct Item: Equatable { public enum Content: Equatable { @@ -27,19 +44,25 @@ public final class AnimatedTextComponent: Component { public let items: [Item] public let noDelay: Bool public let animateScale: Bool + public let preferredDirectionIsDown: Bool + public let blur: Bool public init( font: UIFont, color: UIColor, items: [Item], noDelay: Bool = false, - animateScale: Bool = true + animateScale: Bool = true, + preferredDirectionIsDown: Bool = false, + blur: Bool = false ) { self.font = font self.color = color self.items = items self.noDelay = noDelay self.animateScale = animateScale + self.preferredDirectionIsDown = preferredDirectionIsDown + self.blur = blur } public static func ==(lhs: AnimatedTextComponent, rhs: AnimatedTextComponent) -> Bool { @@ -58,6 +81,12 @@ public final class AnimatedTextComponent: Component { if lhs.animateScale != rhs.animateScale { return false } + if lhs.preferredDirectionIsDown != rhs.preferredDirectionIsDown { + return false + } + if lhs.blur != rhs.blur { + return false + } return true } @@ -88,8 +117,14 @@ public final class AnimatedTextComponent: Component { var size = CGSize() let delayNorm: CGFloat = 0.002 + var offsetNorm: CGFloat = 0.4 + let transitionBlurRadius: CGFloat = 6.0 var firstDelayWidth: CGFloat? + if component.preferredDirectionIsDown { + firstDelayWidth = 0.0 + offsetNorm = 0.8 + } var validKeys: [CharacterKey] = [] for item in component.items { @@ -119,6 +154,57 @@ public final class AnimatedTextComponent: Component { index += 1 validKeys.append(characterKey) + } + } + + var outLastDelayWidth: CGFloat? + var outFirstDelayWidth: CGFloat? + if component.preferredDirectionIsDown { + for (key, characterView) in self.characters { + if !validKeys.contains(key), let characterView = characterView.view { + if let outFirstDelayWidthValue = outFirstDelayWidth { + outFirstDelayWidth = max(outFirstDelayWidthValue, characterView.frame.center.x) + } else { + outFirstDelayWidth = characterView.frame.center.x + } + + if let outLastDelayWidthValue = outLastDelayWidth { + outLastDelayWidth = min(outLastDelayWidthValue, characterView.frame.center.x) + } else { + outLastDelayWidth = characterView.frame.center.x + } + } + } + } + if outLastDelayWidth != nil { + firstDelayWidth = outLastDelayWidth + } + + for item in component.items { + var itemText: [String] = [] + switch item.content { + case let .text(text): + if item.isUnbreakable { + itemText = [text] + } else { + itemText = text.map(String.init) + } + case let .number(value, minDigits): + var valueText: String = "\(value)" + while valueText.count < minDigits { + valueText.insert("0", at: valueText.startIndex) + } + + if item.isUnbreakable { + itemText = [valueText] + } else { + itemText = valueText.map(String.init) + } + } + var index = 0 + for character in itemText { + let characterKey = CharacterKey(itemId: item.id, index: index, value: character) + index += 1 var characterTransition = transition let characterView: ComponentView @@ -155,7 +241,11 @@ public final class AnimatedTextComponent: Component { } else { var delayWidth: Double = 0.0 if let firstDelayWidth { - delayWidth = size.width - firstDelayWidth + if characterFrame.midX > characterComponentView.frame.midX { + delayWidth = 0.0 + } else { + delayWidth = abs(size.width - firstDelayWidth) + } } else { firstDelayWidth = size.width } @@ -170,7 +260,7 @@ public final class AnimatedTextComponent: Component { if animateIn, !transition.animation.isImmediate { var delayWidth: Double = 0.0 - if !component.noDelay { + if !component.noDelay || component.preferredDirectionIsDown { if let firstDelayWidth { delayWidth = size.width - firstDelayWidth } else { @@ -181,7 +271,10 @@ public final class AnimatedTextComponent: Component { if component.animateScale { characterComponentView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring) } - characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * 0.5), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + if component.blur { + ComponentTransition.easeInOut(duration: 0.2).animateBlur(layer: characterComponentView.layer, from: transitionBlurRadius, to: 0.0, delay: delayNorm * delayWidth) + } + characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * offsetNorm), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true) characterComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18, delay: delayNorm * delayWidth) } } @@ -194,8 +287,6 @@ public final class AnimatedTextComponent: Component { let outScaleTransition: ComponentTransition = .spring(duration: 0.4) let outAlphaTransition: ComponentTransition = .easeInOut(duration: 0.18) - var outFirstDelayWidth: CGFloat? - var removedKeys: [CharacterKey] = [] for (key, characterView) in self.characters { if !validKeys.contains(key) { @@ -205,18 +296,28 @@ public final class AnimatedTextComponent: Component { if !transition.animation.isImmediate { var delayWidth: Double = 0.0 if let outFirstDelayWidth { - delayWidth = characterComponentView.frame.minX - outFirstDelayWidth + delayWidth = abs(characterComponentView.frame.midX - outFirstDelayWidth) } else { - outFirstDelayWidth = characterComponentView.frame.minX + outFirstDelayWidth = characterComponentView.frame.midX } + delayWidth = max(0.0, delayWidth) if component.animateScale { outScaleTransition.setScale(view: characterComponentView, scale: 0.01, delay: delayNorm * delayWidth) } - outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: characterComponentView.center.y - characterComponentView.bounds.height * 0.4), delay: delayNorm * delayWidth) + let targetY: CGFloat + if component.preferredDirectionIsDown { + targetY = characterComponentView.center.y - characterComponentView.bounds.height * offsetNorm + } else { + targetY = characterComponentView.center.y - characterComponentView.bounds.height * offsetNorm + } + outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: targetY), delay: delayNorm * delayWidth) outAlphaTransition.setAlpha(view: characterComponentView, alpha: 0.0, delay: delayNorm * delayWidth, completion: { [weak characterComponentView] _ in characterComponentView?.removeFromSuperview() }) + if component.blur { + outAlphaTransition.animateBlur(layer: characterComponentView.layer, from: 0.0, to: transitionBlurRadius, delay: delayNorm * delayWidth, removeOnCompletion: false) + } } else { characterComponentView.removeFromSuperview() } diff --git a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift index 37a7531905..98a8c1e5e9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift @@ -26,13 +26,13 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { private var context: AccountContext? private let containerNode: ContextControllerSourceNode - public let avatarNode: AvatarNode + public var avatarNode: AvatarNode private var avatarVideoNode: AvatarVideoNode? public private(set) var avatarStoryView: ComponentView? public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)? - public let statusView: ComponentView + public var statusView: ComponentView private var starView: StarView? private var cachedDataDisposable = MetaDisposable() @@ -119,6 +119,13 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { public func setPeer(context: AccountContext, theme: PresentationTheme, peer: EnginePeer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) { self.context = context + + if let statusComponentView = self.statusView.view { + self.statusView = ComponentView() + statusComponentView.removeFromSuperview() + } + + self.avatarNode.isHidden = false self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: displayDimensions, storeUnrounded: storeUnrounded) if let peer, peer.isSubscription { diff --git a/submodules/TelegramUI/Components/Chat/ChatBotStartInputPanelNode/Sources/ChatBotStartInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatBotStartInputPanelNode/Sources/ChatBotStartInputPanelNode.swift index f9a983a911..15fee82f2e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatBotStartInputPanelNode/Sources/ChatBotStartInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatBotStartInputPanelNode/Sources/ChatBotStartInputPanelNode.swift @@ -122,7 +122,7 @@ public final class ChatBotStartInputPanelNode: ChatInputPanelNode { self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: 8.0), size: buttonSize) - if !self.tooltipDismissed, let context = self.context { + if !self.tooltipDismissed, let context = self.context, let user = self.presentationInterfaceState?.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, !botInfo.flags.contains(.hasForum) { let absoluteFrame = self.button.view.convert(self.button.bounds, to: nil) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize()) diff --git a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift index 0f5f59ab92..aa000a8a3f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift @@ -19,8 +19,9 @@ public struct ChatMessageEntryAttributes: Equatable { public var isPlaying: Bool public var isCentered: Bool public var authorStoryStats: PeerStoryStats? + public var displayContinueThreadFooter: Bool - public init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?, isPlaying: Bool, isCentered: Bool, authorStoryStats: PeerStoryStats?) { + public init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?, isPlaying: Bool, isCentered: Bool, authorStoryStats: PeerStoryStats?, displayContinueThreadFooter: Bool) { self.rank = rank self.isContact = isContact self.contentTypeHint = contentTypeHint @@ -28,6 +29,7 @@ public struct ChatMessageEntryAttributes: Equatable { self.isPlaying = isPlaying self.isCentered = isCentered self.authorStoryStats = authorStoryStats + self.displayContinueThreadFooter = displayContinueThreadFooter } public init() { @@ -38,12 +40,14 @@ public struct ChatMessageEntryAttributes: Equatable { self.isPlaying = false self.isCentered = false self.authorStoryStats = nil + self.displayContinueThreadFooter = false } } public enum ChatInfoData: Equatable { case botInfo(title: String, text: String, photo: TelegramMediaImage?, video: TelegramMediaFile?) case userInfo(peer: EnginePeer, verification: PeerVerification?, registrationDate: String?, phoneCountry: String?, groupsInCommonCount: Int32) + case newThreadInfo } public enum ChatHistoryEntry: Identifiable, Comparable { @@ -90,8 +94,13 @@ public enum ChatHistoryEntry: Identifiable, Comparable { return index case let .ReplyCountEntry(index, _, _, _): return index - case .ChatInfoEntry: - return MessageIndex.absoluteLowerBound() + case let .ChatInfoEntry(infoData, _): + switch infoData { + case .newThreadInfo: + return MessageIndex.absoluteUpperBound() + default: + return MessageIndex.absoluteLowerBound() + } case .SearchEntry: return MessageIndex.absoluteLowerBound() } @@ -107,8 +116,13 @@ public enum ChatHistoryEntry: Identifiable, Comparable { return index case let .ReplyCountEntry(index, _, _, _): return index - case .ChatInfoEntry: - return MessageIndex.absoluteLowerBound() + case let .ChatInfoEntry(infoData, _): + switch infoData { + case .newThreadInfo: + return MessageIndex.absoluteUpperBound() + default: + return MessageIndex.absoluteLowerBound() + } case .SearchEntry: return MessageIndex.absoluteLowerBound() } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift index 9aa7f29a9b..4ce3474be7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift @@ -235,6 +235,8 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { iconImage = PresentationResourcesChat.messageButtonsPostApprove(theme.theme) case .suggestedPostEdit: iconImage = PresentationResourcesChat.messageButtonsPostEdit(theme.theme) + case .actionArrow: + iconImage = PresentationResourcesChat.chatBubbleArrowFreeImage(theme.theme) } tintColor = titleColor } else { @@ -453,7 +455,11 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { var titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.size.width) / 2.0), y: floor((42.0 - titleSize.size.height) / 2.0) + 1.0), size: titleSize.size) if let image = node.iconNode?.image, customInfo?.icon != nil { - titleFrame.origin.x = floorToScreenPixels((width - titleSize.size.width - image.size.width - 3.0) * 0.5) + 3.0 + image.size.width + if customInfo?.icon == .actionArrow { + titleFrame.origin.x = floorToScreenPixels((width - titleSize.size.width - image.size.width + 1.0) * 0.5) - 0.0 + } else { + titleFrame.origin.x = floorToScreenPixels((width - titleSize.size.width - image.size.width - 3.0) * 0.5) + 3.0 + image.size.width + } } titleNode.layer.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) animation.animator.updatePosition(layer: titleNode.layer, position: CGPoint(x: titleFrame.midX, y: titleFrame.midY), completion: nil) @@ -464,7 +470,11 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { if let iconNode = node.iconNode { let iconFrame: CGRect if customInfo?.icon != nil, let image = iconNode.image { - iconFrame = CGRect(x: titleFrame.minX - 3.0 - image.size.width, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - image.size.height) * 0.5) - 1.0, width: image.size.width, height: image.size.height) + if customInfo?.icon == .actionArrow { + iconFrame = CGRect(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - image.size.height) * 0.5) - 1.0, width: image.size.width, height: image.size.height) + } else { + iconFrame = CGRect(x: titleFrame.minX - 3.0 - image.size.width, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - image.size.height) * 0.5) - 1.0, width: image.size.width, height: image.size.height) + } } else { iconFrame = CGRect(x: width - 16.0, y: 4.0, width: 12.0, height: 12.0) } @@ -502,6 +512,7 @@ public final class ChatMessageActionButtonsNode: ASDisplayNode { case suggestedPostApprove case suggestedPostReject case suggestedPostEdit + case actionArrow } public struct CustomInfo { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift index 66f837bc0d..a7a021d09b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift @@ -224,6 +224,7 @@ open class ChatMessageBubbleContentNode: ASDisplayNode { public var updateIsTextSelectionActive: ((Bool) -> Void)? public var requestInlineUpdate: (() -> Void)? + public var requestFullUpdate: (() -> Void)? open var disablesClipping: Bool { return false diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 23ad6ae540..57dcfda475 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -241,6 +241,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ } else if case .joinedChannel = action.action { result.append((message, ChatMessageJoinedChannelBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) needReactions = false + } else if case let .setChatTheme(chatTheme) = action.action, case .gift = chatTheme { + result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) } else { if !canAddMessageReactions(message: message) { needReactions = false @@ -955,6 +957,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI process(node: self) } + override public func insertionAnimationDuration() -> Double? { + return nil + } + + override public func updateAnimationDuration() -> Double? { + return nil + } + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) { super.animateInsertion(currentTimestamp, duration: duration, options: options) @@ -2670,7 +2680,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI }*/ var hasReply = replyMessage != nil || replyForward != nil || replyStory != nil - if !isInstantVideo, hasThreadInfo { + if !isInstantVideo && hasThreadInfo { if let threadId = item.message.threadId, let replyMessage = replyMessage, Int64(replyMessage.id.id) == threadId { hasReply = false } @@ -2701,6 +2711,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI headerSize.height += threadInfoSizeApply.0.height + 5.0 }*/ } + if !isInstantVideo, hasReply, let threadId = item.message.threadId, let replyMessage, replyQuote == nil, case let .replyThread(replyThread) = item.chatLocation, replyThread.isChannelPost, replyMessage.id.id == Int32(clamping: threadId) { + hasReply = false + } if !isInstantVideo, hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) { if headerSize.height.isZero { @@ -2864,6 +2877,37 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, item.controllerInteraction.presentationContext.backgroundNode, replyMarkup, [:], item.message, maximumNodeWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout + } else if item.content.firstMessageAttributes.displayContinueThreadFooter { + var buttonValue: UInt8 = 3 + let button = MemoryBuffer(data: Data(bytes: &buttonValue, count: 1)) + + let customInfos: [MemoryBuffer: ChatMessageActionButtonsNode.CustomInfo] = [ + button: ChatMessageActionButtonsNode.CustomInfo( + isEnabled: true, + icon: .actionArrow + ), + ] + + //TODO:localize + let (minWidth, buttonsLayout) = actionButtonsLayout( + item.context, + item.presentationData.theme, + item.presentationData.chatBubbleCorners, + item.presentationData.strings, + item.controllerInteraction.presentationContext.backgroundNode, + ReplyMarkupMessageAttribute( + rows: [ + ReplyMarkupRow(buttons: [ + ReplyMarkupButton(title: "Continue last thread", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: button)) + ]) + ], + flags: [], + placeholder: nil + ), customInfos, item.message, baseWidth) + maxContentWidth = max(maxContentWidth, minWidth) + actionButtonsFinalize = buttonsLayout + + lastNodeTopPosition = .None(.Both) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? @@ -4459,6 +4503,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.internalUpdateLayout() } + contentNode.requestFullUpdate = { [weak strongSelf] in + guard let strongSelf, let item = strongSelf.item else { + return + } + + item.controllerInteraction.requestMessageUpdate(item.message.id, false) + } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/BUILD index b5039cdd81..81bce800e8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/BUILD @@ -33,6 +33,7 @@ swift_library( "//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/InvisibleInkDustNode", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent", + "//submodules/TelegramUI/Components/Gifts/GiftItemComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 18a4c0319a..838412e9d1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -24,6 +24,7 @@ import ChatMessageItemCommon import TextNodeWithEntities import InvisibleInkDustNode import PeerInfoCoverComponent +import GiftItemComponent private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? { return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true) @@ -45,6 +46,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { private var dustNode: InvisibleInkDustNode? private let placeholderNode: StickerShimmerEffectNode private let animationNode: AnimatedStickerNode + private let giftIcon = ComponentView() private let modelTitleTextNode: TextNode private let modelValueTextNode: TextNode @@ -274,9 +276,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { for media in item.message.media { if let action = media as? TelegramMediaAction { switch action.action { - case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): releasedBy = gift.releasedBy - case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _): + case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _, _): releasedBy = gift.releasedBy default: break @@ -416,6 +418,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { var months: Int32 = 3 var animationName: String = "" var animationFile: TelegramMediaFile? + var uniqueGift: StarGift.UniqueGift? var title = item.presentationData.strings.Notification_PremiumGift_Title var text = "" var subtitleColor = primaryTextColor @@ -547,7 +550,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View hasServiceMessage = false } - case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, channelPeerId, senderPeerId, _): + case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, isPrepaidUpgrade, _, channelPeerId, senderPeerId, _, _, _, _): if case let .generic(gift) = gift { if let releasedBy = gift.releasedBy, let peer = item.message.peers[releasedBy], let addressName = peer.addressName { creatorButtonTitle = item.presentationData.strings.Notification_StarGift_ReleasedBy("**@\(addressName)**").string @@ -561,10 +564,14 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { if isSelfGift { title = item.presentationData.strings.Notification_StarGift_Self_Title } else { - if let senderPeerId, let name = item.message.peers[senderPeerId].flatMap(EnginePeer.init)?.compactDisplayTitle { - authorName = name + if isPrepaidUpgrade && senderPeerId == channelPeerId { + title = item.presentationData.strings.Gift_View_Unknown_Title + } else { + if let senderPeerId, let name = item.message.peers[senderPeerId].flatMap(EnginePeer.init)?.compactDisplayTitle { + authorName = name + } + title = item.presentationData.strings.Notification_StarGift_Title(authorName).string } - title = item.presentationData.strings.Notification_StarGift_Title(authorName).string } if let giftText, !giftText.isEmpty { text = giftText @@ -611,7 +618,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle } } else { - if upgradeStars != nil { + if isPrepaidUpgrade { + text = item.presentationData.strings.Notification_StarGift_Subtitle_Upgrade_Prepaid(peerName).string + } else if upgradeStars != nil { text = item.presentationData.strings.Notification_StarGift_Subtitle_Upgrade_Other(peerName).string } else if let convertStars, convertStars > 0 { let starsString = item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(clamping: convertStars)).replacingOccurrences(of: " ", with: "\u{00A0}") @@ -641,7 +650,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { buttonTitle = item.presentationData.strings.Notification_StarGift_View } } - case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _): + case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _, _): if case let .unique(uniqueGift) = gift { isStarGift = true @@ -668,8 +677,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { title = item.presentationData.strings.Notification_StarGift_TitleShort } else { title = item.presentationData.strings.Notification_StarGift_Title(authorName).string - } - text = isStoryEntity ? "**\(item.presentationData.strings.Notification_StarGift_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, item.presentationData.dateTimeFormat.groupingSeparator))**" : "**\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, item.presentationData.dateTimeFormat.groupingSeparator))**" + } + text = isStoryEntity ? "**\(item.presentationData.strings.Notification_StarGift_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: item.presentationData.dateTimeFormat))**" : "**\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: item.presentationData.dateTimeFormat))**" ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift buttonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_View modelTitle = item.presentationData.strings.Notification_StarGift_Model @@ -702,6 +711,20 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { text = item.presentationData.strings.Notification_StarGift_Subtitle_Refunded animationFile = gift.file } + case let .setChatTheme(chatTheme): + title = "" + var giftTitle = "" + if case let .gift(gift, _) = chatTheme, case let .unique(uniqueGiftValue) = gift { + giftTitle = "\(uniqueGiftValue.title) #\(formatCollectibleNumber(uniqueGiftValue.number, dateTimeFormat: item.presentationData.dateTimeFormat))" + uniqueGift = uniqueGiftValue + } + if incoming { + let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? "" + text = item.presentationData.strings.Notification_ChatTheme_Text(authorName, giftTitle).string + } else { + text = item.presentationData.strings.Notification_ChatTheme_TextYou(giftTitle).string + } + hasServiceMessage = false default: break } @@ -860,6 +883,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { giftSize.height += 12.0 } + if let _ = uniqueGift { + giftSize.height -= 31.0 + } + var labelRects = labelLayout.linesRects() if labelRects.count > 1 { let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width }) @@ -938,7 +965,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.creatorButtonNode.isUserInteractionEnabled = !item.presentationData.isPreview strongSelf.creatorButtonTitleNode.isHidden = creatorButtonTitle.isEmpty - if strongSelf.item == nil && !isStoryEntity { + if strongSelf.item == nil && !isStoryEntity && uniqueGift == nil { strongSelf.animationNode.started = { [weak self] in if let strongSelf = self { let current = CACurrentMediaTime() @@ -1003,7 +1030,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size) strongSelf.titleNode.frame = titleFrame - let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight)) + var clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight)) + if let _ = uniqueGift { + clippingTextFrame.origin.y -= 23.0 + } var attributesOffsetY: CGFloat = 0.0 @@ -1309,6 +1339,31 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { } } + if let uniqueGift { + let iconSize = CGSize(width: 94.0, height: 94.0) + let _ = strongSelf.giftIcon.update( + transition: .immediate, + component: AnyComponent(GiftItemComponent( + context: item.context, + theme: item.presentationData.theme.theme, + strings: item.presentationData.strings, + peer: nil, + subject: .uniqueGift(gift: uniqueGift, price: nil), + mode: .thumbnail + )), + environment: {}, + containerSize: iconSize + ) + if let giftIconView = strongSelf.giftIcon.view { + if giftIconView.superview == nil { + // backgroundView.layer.cornerRadius = 20.0 + //backgroundView.clipsToBounds = true + strongSelf.view.addSubview(giftIconView) + } + giftIconView.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY + 17.0), size: iconSize) + } + } + let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0) if let (offset, image) = backgroundMaskImage { if strongSelf.backgroundNode == nil { @@ -1518,7 +1573,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { if isPlaying { var alreadySeen = true - if item.message.flags.contains(.Incoming) { + if let action = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case .setChatTheme = action.action { + + } else if item.message.flags.contains(.Incoming) { if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] { if unreadRange.contains(item.message.id.id) { alreadySeen = false diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index e5e3182af4..60857c7020 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -623,15 +623,15 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { |> map { resourceStatus, actualFetchStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in return (resourceStatus, actualFetchStatus) } - updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false) + updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false, isSavedMusic: false) } else { updatedStatusSignal = messageFileMediaResourceStatus(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions) |> map { resourceStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in return (resourceStatus, nil) } - updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false) + updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false, isSavedMusic: false) } - updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false) + updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: EngineMessage(arguments.message), isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false, isSavedMusic: false) } var isAudio = false diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 1ec33a7f1c..b4605b04f0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1469,7 +1469,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { playbackStatusNode.position = playbackStatusFrame.center } - let status = messageFileMediaPlaybackStatus(context: item.context, file: file, message: EngineMessage(item.message), isRecentActions: item.associatedData.isRecentActions, isGlobalSearch: false, isDownloadList: false) + let status = messageFileMediaPlaybackStatus(context: item.context, file: file, message: EngineMessage(item.message), isRecentActions: item.associatedData.isRecentActions, isGlobalSearch: false, isDownloadList: false, isSavedMusic: false) playbackStatusNode.status = status self.durationNode?.status = status |> map(Optional.init) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index ef67f9f5ec..37ae63c442 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -1656,7 +1656,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .screen) |> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in if let value = value { - return .single(value) + return .single(value.generator) } else { return .complete() } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift index 173aa77857..c0770c076b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift @@ -479,7 +479,7 @@ private final class ChatMessagePeerContentNode: ASDisplayNode { avatarIconContent = .topic(title: String(info.title.prefix(1)), color: info.iconColor, size: CGSize(width: 16.0, height: 16.0)) } } else { - avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(presentationData.theme.theme)?.withRenderingMode(.alwaysTemplate), tintColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper)) + avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(presentationData.theme.theme), tintColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper)) } let avatarIconComponent = EmojiStatusComponent( diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index 86ba2a985c..1f6409506d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -345,6 +345,15 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible if let threadInfo = content.firstMessage.associatedThreadInfo { headerSeparableThreadId = content.firstMessage.threadId headerDisplayPeer = ChatMessageDateHeader.HeaderData(contents: .thread(id: threadId, info: threadInfo)) + } else if content.firstMessage.threadId == EngineMessage.newTopicThreadId { + headerSeparableThreadId = content.firstMessage.threadId + //TODO:localize + headerDisplayPeer = ChatMessageDateHeader.HeaderData(contents: .thread(id: threadId, info: Message.AssociatedThreadInfo( + title: "New Chat", + icon: nil, + iconColor: 0, + isClosed: false + ))) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 45b69ec4de..990c1fa9d8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -113,6 +113,35 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private var appliedExpandedBlockIds: Set? private var displayContentsUnderSpoilers: (value: Bool, location: CGPoint?) = (false, nil) + private final class TextRevealAnimationState { + let fromCount: Int + let toCount: Int + let startTimestamp: Double + let duration: Double + + init(fromCount: Int, toCount: Int, startTimestamp: Double, duration: Double) { + self.fromCount = fromCount + self.toCount = toCount + self.startTimestamp = startTimestamp + self.duration = duration + } + + func fraction(timestamp: Double) -> CGFloat { + var animationFraction = (timestamp - self.startTimestamp) / self.duration + animationFraction = max(0.0, min(1.0, animationFraction)) + return animationFraction + } + + func glyphCount(timestamp: Double) -> Int { + let animationFraction = self.fraction(timestamp: timestamp) + let glyphCount = (1.0 - animationFraction) * Double(self.fromCount) + animationFraction * Double(self.toCount) + return Int(glyphCount) + } + } + + private var textRevealLink: SharedDisplayLinkDriver.Link? + private var textRevealAnimationState: TextRevealAnimationState? + override public var visibility: ListViewItemNodeVisibility { didSet { if oldValue != self.visibility { @@ -131,6 +160,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { required public init() { self.containerNode = ASDisplayNode() + self.containerNode.clipsToBounds = true self.textNode = InteractiveTextNodeWithEntities() @@ -200,12 +230,22 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { + let previousItem = self.item + let textLayout = InteractiveTextNodeWithEntities.asyncLayout(self.textNode) let statusLayout = ChatMessageDateAndStatusNode.asyncLayout(self.statusNode) let currentCachedChatMessageText = self.cachedChatMessageText let expandedBlockIds = self.expandedBlockIds let displayContentsUnderSpoilers = self.displayContentsUnderSpoilers + let currentMaxGlyphCount: Int? + if let textRevealAnimationState = self.textRevealAnimationState { + currentMaxGlyphCount = textRevealAnimationState.glyphCount(timestamp: CACurrentMediaTime()) + //print("currentMaxGlyphCount(\(textRevealAnimationState.fromCount) -> \(textRevealAnimationState.toCount)) fraction: \(textRevealAnimationState.fraction(timestamp: CACurrentMediaTime()))") + } else { + currentMaxGlyphCount = nil + } + let previousGlyphCount = self.textNode.textNode.getGlyphCount() return { item, layoutConstants, _, _, _, _ in let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) @@ -673,9 +713,32 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } var textFrame = CGRect(origin: CGPoint(x: -textInsets.left, y: -textInsets.top), size: textLayout.size) + var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom)) textFrame = textFrame.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: topInset) + let realTextFrame = textFrame + + var hasDraft = false + if item.message.attributes.contains(where: { $0 is TypingDraftMessageAttribute }) { + hasDraft = true + } + var hadDraft = false + if let previousItem, previousItem.message.attributes.contains(where: { $0 is TypingDraftMessageAttribute }) { + hadDraft = true + } + + var maxGlyphCount = currentMaxGlyphCount + if maxGlyphCount == nil && (hasDraft || hadDraft) { + maxGlyphCount = previousGlyphCount + } + + if let maxGlyphCount { + textFrame.size = textLayout.sizeForGlyphCount(glyphCount: maxGlyphCount) + //print("currentMaxGlyphCount: \(currentMaxGlyphCount), size: \(textFrame.size.height)") + textFrameWithoutInsets.size = CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom) + } + textFrameWithoutInsets = textFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: topInset) var suggestedBoundingWidth: CGFloat = textFrameWithoutInsets.width @@ -706,8 +769,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.cachedChatMessageText = updatedCachedChatMessageText } + var previousAnimateGlyphCount: Int? + if hasDraft || hadDraft { + previousAnimateGlyphCount = strongSelf.textNode.textNode.getGlyphCount() + } + strongSelf.textNode.textNode.displaysAsynchronously = !item.presentationData.isPreview - strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: boundingSize) + animation.animator.updateFrame(layer: strongSelf.containerNode.layer, frame: CGRect(origin: CGPoint(), size: boundingSize), completion: nil) if strongSelf.appliedExpandedBlockIds != nil && strongSelf.appliedExpandedBlockIds != strongSelf.expandedBlockIds { itemApply?.setInvertOffsetDirection() @@ -764,7 +832,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } ) )) - animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: textFrame, completion: nil) + animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: realTextFrame, completion: nil) switch strongSelf.visibility { case .none: @@ -785,8 +853,6 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } strongSelf.textAccessibilityOverlayNode.frame = textFrame - //TODO:release - //strongSelf.textAccessibilityOverlayNode.cachedLayout = textLayout strongSelf.updateIsTranslating(isTranslating) @@ -897,6 +963,10 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.codeHighlightState = nil codeHighlightState.disposable.dispose() } + + if previousAnimateGlyphCount != nil || strongSelf.textRevealAnimationState != nil || hadDraft { + strongSelf.updateTextRevealAnimation(previousGlyphCount: previousAnimateGlyphCount ?? 0) + } } }) }) @@ -904,6 +974,74 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } + private func updateTextRevealAnimation(previousGlyphCount: Int) { + var fromCount = previousGlyphCount + let toCount = self.textNode.textNode.getGlyphCount() + let timestamp = CACurrentMediaTime() + if let textRevealAnimationState = self.textRevealAnimationState { + if textRevealAnimationState.toCount == toCount { + return + } + fromCount = textRevealAnimationState.glyphCount(timestamp: timestamp) + } + if fromCount == toCount { + if self.textRevealAnimationState != nil { + self.textRevealAnimationState = nil + self.textRevealLink = nil + self.textNode.textNode.updateRevealGlyphCount(count: nil) + } + return + } + + var duration: Double = Double(toCount - fromCount) / 20.0 + duration = max(0.1, min(duration, 5.0)) + + self.textRevealAnimationState = TextRevealAnimationState( + fromCount: fromCount, + toCount: toCount, + startTimestamp: timestamp, + duration: duration + ) + if self.textRevealLink == nil, self.textRevealAnimationState != nil { + var lastLineUpdateTimestamp = timestamp + self.textRevealLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in + guard let self else { + return + } + guard let textRevealAnimationState = self.textRevealAnimationState else { + self.textRevealLink = nil + return + } + let timestamp = CACurrentMediaTime() + if textRevealAnimationState.fraction(timestamp: timestamp) >= 1.0 { + self.textRevealAnimationState = nil + self.textRevealLink = nil + + self.textNode.textNode.updateRevealGlyphCount(count: nil) + self.requestFullUpdate?() + } else { + let lineUpdateTimeout = timestamp - lastLineUpdateTimestamp + + var requestUpdate = false + let glyphCount = textRevealAnimationState.glyphCount(timestamp: timestamp) + if let revealGlyphCount = self.textNode.textNode.revealGlyphCount, let cachedLayout = self.textNode.textNode.cachedLayout { + if cachedLayout.sizeForGlyphCount(glyphCount: revealGlyphCount).height != cachedLayout.sizeForGlyphCount(glyphCount: glyphCount).height { + if lineUpdateTimeout >= 0.1 { + lastLineUpdateTimestamp = timestamp + requestUpdate = true + } + } + } + self.textNode.textNode.updateRevealGlyphCount(count: glyphCount) + + if requestUpdate { + self.requestFullUpdate?() + } + } + } + } + } + override public func animateInsertion(_ currentTimestamp: Double, duration: Double) { self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.statusNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -1569,7 +1707,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { guard let self, completed else { return } - self.containerNode.clipsToBounds = false + self.containerNode.clipsToBounds = true }) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift index 290a9c7f39..ec9e6380ea 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift @@ -387,7 +387,7 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode updateImageSignal = patternWallpaperImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, representations: representations, mode: .thumbnail) |> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in if let value { - return .single(value) + return .single(value.generator) } else { return .complete() } diff --git a/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/BUILD b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/BUILD new file mode 100644 index 0000000000..ee2fec7f98 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/BUILD @@ -0,0 +1,31 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatNewThreadInfoItem", + module_name = "ChatNewThreadInfoItem", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/TextFormat", + "//submodules/AccountContext", + "//submodules/WallpaperBackgroundNode", + "//submodules/TelegramUI/Components/ChatControllerInteraction", + "//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode", + "//submodules/TelegramStringFormatting", + "//submodules/ComponentFlow", + "//submodules/Components/BundleIconComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift new file mode 100644 index 0000000000..3faae56d1b --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift @@ -0,0 +1,326 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TextFormat +import AccountContext +import WallpaperBackgroundNode +import ChatControllerInteraction +import ChatMessageBubbleContentNode +import TelegramStringFormatting +import ChatControllerInteraction +import ComponentFlow +import BundleIconComponent + +public final class ChatNewThreadInfoItem: ListViewItem { + fileprivate let controllerInteraction: ChatControllerInteraction + fileprivate let presentationData: ChatPresentationData + fileprivate let context: AccountContext + + public init( + controllerInteraction: ChatControllerInteraction, + presentationData: ChatPresentationData, + context: AccountContext + ) { + self.controllerInteraction = controllerInteraction + self.presentationData = presentationData + self.context = context + } + + public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + let configure = { + let node = ChatNewThreadInfoItemNode() + + let nodeLayout = node.asyncLayout() + let (layout, apply) = nodeLayout(self, params) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply(.None) }) + }) + } + } + if Thread.isMainThread { + async { + configure() + } + } else { + configure() + } + } + + public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? ChatNewThreadInfoItemNode { + let nodeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = nodeLayout(self, params) + Queue.mainQueue().async { + completion(layout, { _ in + apply(animation) + }) + } + } + } + } + } +} + +public final class ChatNewThreadInfoItemNode: ListViewItemNode, ASGestureRecognizerDelegate { + public var controllerInteraction: ChatControllerInteraction? + + public let offsetContainer: ASDisplayNode + public let titleNode: TextNode + public let subtitleNode: TextNode + let arrowView: UIImageView + let iconBackground: SimpleLayer + var icon = ComponentView() + + private var theme: ChatPresentationThemeData? + + private var wallpaperBackgroundNode: WallpaperBackgroundNode? + private var backgroundContent: WallpaperBubbleBackgroundNode? + + private var absolutePosition: (CGRect, CGSize)? + + private var item: ChatNewThreadInfoItem? + + public init() { + self.offsetContainer = ASDisplayNode() + + self.iconBackground = SimpleLayer() + + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + self.titleNode.displaysAsynchronously = false + + self.subtitleNode = TextNode() + self.subtitleNode.isUserInteractionEnabled = false + self.subtitleNode.displaysAsynchronously = false + + self.arrowView = UIImageView() + + super.init(layerBacked: false, dynamicBounce: true, rotated: true) + + self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) + + self.addSubnode(self.offsetContainer) + self.offsetContainer.addSubnode(self.titleNode) + self.offsetContainer.addSubnode(self.subtitleNode) + } + + override public func didLoad() { + super.didLoad() + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + tapRecognizer.delegate = self.wrappedGestureRecognizerDelegate + self.offsetContainer.view.addGestureRecognizer(tapRecognizer) + } + + public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer.view === self.offsetContainer.view { + let location = gestureRecognizer.location(in: self.offsetContainer.view) + if let backgroundContent = self.backgroundContent, backgroundContent.frame.contains(location) { + return true + } + return false + } + return true + } + + @objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) { + if let item = self.item { + item.controllerInteraction.updateInputMode { mode in + if case .none = mode { + return .text + } else { + return mode + } + } + } + } + + override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + super.updateAbsoluteRect(rect, within: containerSize) + + self.absolutePosition = (rect, containerSize) + if let backgroundContent = self.backgroundContent { + var backgroundFrame = backgroundContent.frame + backgroundFrame.origin.x += rect.minX + backgroundFrame.origin.y += containerSize.height - rect.minY + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) + } + } + + public func asyncLayout() -> (_ item: ChatNewThreadInfoItem, _ width: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode) + + let currentItem = self.item + + return { [weak self] item, params in + let themeUpdated = item.presentationData.theme !== currentItem?.presentationData.theme + + var backgroundSize = CGSize(width: 240.0, height: 0.0) + + let verticalItemInset: CGFloat = 10.0 + let horizontalInset: CGFloat = 16.0 + params.leftInset + let horizontalContentInset: CGFloat = 16.0 + let topInset: CGFloat = 15.0 + let bottomInset: CGFloat = 21.0 + let verticalSpacing: CGFloat = 6.0 + let iconBackgroundSize: CGFloat = 80.0 + let iconTextSpacing: CGFloat = 14.0 + + let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText + let subtitleColor = primaryTextColor + + backgroundSize.height += topInset + + let constrainedWidth = params.width - (horizontalInset + horizontalContentInset) * 2.0 + + //TODO:localize + let titleString = "New Thread" + let subtitleString = "Type any message to\ncreate a new thread." + + backgroundSize.height += iconBackgroundSize + backgroundSize.height += iconTextSpacing + + let titleConstrainedWidth = constrainedWidth + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: Font.semibold(15.0), textColor: primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: titleConstrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + backgroundSize.height += titleLayout.size.height + backgroundSize.height += verticalSpacing + + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitleString, font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: titleConstrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, lineSpacing: 0.2, cutout: nil, insets: UIEdgeInsets())) + backgroundSize.height += subtitleLayout.size.height + backgroundSize.height += 10.0 + + backgroundSize.width = horizontalContentInset * 2.0 + max(titleLayout.size.width, subtitleLayout.size.width) + + backgroundSize.height += bottomInset + + let backgroundFrame = CGRect(origin: CGPoint(x: floor((params.width - backgroundSize.width) / 2.0), y: verticalItemInset + 4.0), size: backgroundSize) + + let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: backgroundSize.height + verticalItemInset * 2.0 + 10.0), insets: UIEdgeInsets()) + return (itemLayout, { _ in + if let strongSelf = self { + strongSelf.item = item + strongSelf.theme = item.presentationData.theme + + if themeUpdated { + } + + strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: itemLayout.contentSize) + + var contentOriginY = backgroundFrame.origin.y + topInset + + let iconBackgroundFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width - iconBackgroundSize) * 0.5), y: contentOriginY), size: CGSize(width: iconBackgroundSize, height: iconBackgroundSize)) + strongSelf.iconBackground.frame = iconBackgroundFrame + strongSelf.iconBackground.cornerRadius = iconBackgroundSize * 0.5 + strongSelf.iconBackground.backgroundColor = (item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)).cgColor + contentOriginY += iconBackgroundSize + contentOriginY += iconTextSpacing + + if strongSelf.iconBackground.superlayer == nil { + strongSelf.offsetContainer.layer.addSublayer(strongSelf.iconBackground) + } + if strongSelf.arrowView.superview == nil { + strongSelf.offsetContainer.view.addSubview(strongSelf.arrowView) + } + + let iconComponent = AnyComponent(BundleIconComponent( + name: "Chat/Empty Chat/ChannelMessages", + tintColor: primaryTextColor + )) + let iconSize = strongSelf.icon.update( + transition: .immediate, + component: iconComponent, + environment: {}, + containerSize: CGSize(width: 50.0, height: 50.0) + ) + let iconFrame = CGRect(origin: CGPoint(x: iconBackgroundFrame.minX + floor((iconBackgroundFrame.width - iconSize.width) * 0.5), y: iconBackgroundFrame.minY + floor((iconBackgroundFrame.height - iconSize.height) * 0.5)), size: iconSize) + if let iconView = strongSelf.icon.view { + if iconView.superview == nil { + strongSelf.offsetContainer.view.addSubview(iconView) + } + iconView.frame = iconFrame + } + + let _ = titleApply() + let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + floor((backgroundSize.width - titleLayout.size.width) / 2.0), y: contentOriginY), size: titleLayout.size) + strongSelf.titleNode.frame = titleFrame + contentOriginY += titleLayout.size.height + contentOriginY += verticalSpacing + + let _ = subtitleApply() + let subtitleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + floor((backgroundSize.width - subtitleLayout.size.width) / 2.0), y: contentOriginY), size: subtitleLayout.size) + strongSelf.subtitleNode.frame = subtitleFrame + contentOriginY += subtitleLayout.size.height + contentOriginY += 20.0 + + if strongSelf.arrowView.image == nil { + strongSelf.arrowView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Search/DownButton"), color: .white)?.withRenderingMode(.alwaysTemplate) + } + strongSelf.arrowView.tintColor = primaryTextColor.withMultipliedAlpha(0.5) + if let image = strongSelf.arrowView.image { + let scaleFactor: CGFloat = 0.8 + let imageSize = CGSize(width: floor(image.size.width * scaleFactor), height: floor(image.size.height * scaleFactor)) + let arrowFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + floor((backgroundSize.width - imageSize.width) / 2.0), y: backgroundFrame.minY + backgroundFrame.height - 8.0 - imageSize.height), size: imageSize) + strongSelf.arrowView.frame = arrowFrame + } + + if strongSelf.backgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + backgroundContent.clipsToBounds = true + strongSelf.backgroundContent = backgroundContent + strongSelf.offsetContainer.insertSubnode(backgroundContent, at: 0) + } + + if let backgroundContent = strongSelf.backgroundContent { + backgroundContent.cornerRadius = item.presentationData.chatBubbleCorners.mainRadius + backgroundContent.frame = backgroundFrame + if let (rect, containerSize) = strongSelf.absolutePosition { + var backgroundFrame = backgroundContent.frame + backgroundFrame.origin.x += rect.minX + backgroundFrame.origin.y += containerSize.height - rect.minY + backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate) + } + } + } + }) + } + } + + override public func updateTrailingItemSpace(_ height: CGFloat, transition: ContainedViewLayoutTransition) { + /*if height.isLessThanOrEqualTo(0.0) { + transition.updateFrame(node: self.offsetContainer, frame: CGRect(origin: CGPoint(), size: self.offsetContainer.bounds.size)) + } else { + transition.updateFrame(node: self.offsetContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: -floorToScreenPixels(height / 2.0)), size: self.offsetContainer.bounds.size)) + }*/ + } + + override public func animateAdded(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false) + } + + override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + let result = super.point(inside: point, with: event) + let extra = self.offsetContainer.frame.contains(point) + return result || extra + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift index e73799fb9f..1871012fc6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift @@ -1041,7 +1041,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg let sharedData = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) |> take(1) if case let .peer(peer, _, _) = controller.subject, peer.id != self.context.account.peerId { - let themeEmoticon = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThemeEmoticon(id: peer.id)) + let themeEmoticon = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ChatTheme(id: peer.id)) initiallySelectedEmoticon = combineLatest(themeEmoticon, sharedData) |> map { themeEmoticon, sharedData -> String in let themeSettings: PresentationThemeSettings @@ -1051,8 +1051,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg themeSettings = PresentationThemeSettings.defaultSettings } let currentDefaultEmoticon = themeSettings.theme.emoticon ?? defaultEmoticon - - return themeEmoticon ?? currentDefaultEmoticon + return themeEmoticon?.id ?? currentDefaultEmoticon } } else { initiallySelectedEmoticon = sharedData diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index 395378a09d..d267817ca8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -189,31 +189,43 @@ public final class ChatRecentActionsController: TelegramBaseController { self.titleView.title = CounterControllerTitle(title: EnginePeer(peer).compactDisplayTitle, counter: self.presentationData.strings.Channel_AdminLog_TitleAllEvents) - let themeEmoticon = self.context.account.postbox.peerView(id: peer.id) - |> map { view -> String? in + let chatTheme = self.context.account.postbox.peerView(id: peer.id) + |> map { view -> ChatTheme? in let cachedData = view.cachedData if let cachedData = cachedData as? CachedUserData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedData as? CachedGroupData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedData as? CachedChannelData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else { return nil } } |> distinctUntilChanged - self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: true), themeEmoticon).startStrict(next: { [weak self] presentationData, chatThemes, themeEmoticon in + self.presentationDataDisposable = combineLatest( + queue: Queue.mainQueue(), + context.sharedContext.presentationData, + context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: true), + chatTheme + ).startStrict(next: { [weak self] presentationData, chatThemes, chatTheme in if let strongSelf = self { let previousTheme = strongSelf.presentationData.theme let previousStrings = strongSelf.presentationData.strings var presentationData = presentationData - if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoticon == themeEmoticon }) { - if let theme = makePresentationTheme(cloudTheme: theme, dark: presentationData.theme.overallDarkAppearance) { - presentationData = presentationData.withUpdated(theme: theme) - presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + if let chatTheme { + switch chatTheme { + case let .emoticon(emoticon): + if let theme = chatThemes.first(where: { $0.emoticon == emoticon }), let theme = makePresentationTheme(cloudTheme: theme, dark: presentationData.theme.overallDarkAppearance) { + presentationData = presentationData.withUpdated(theme: theme) + presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + } + case let .gift(gift, wallpaper): + let _ = gift + let _ = wallpaper + //TODO:release } } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index 9e7a6a6e9f..e5349d58ed 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -1293,7 +1293,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { if let photoRepresentation = invite.photoRepresentation { photo.append(photoRepresentation) } - let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil) + let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil) let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount.value, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0) inputData.set(.single(BotCheckoutController.InputData( @@ -1462,6 +1462,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { break case .giftCollection: break + case .sendGift: + break } } })) diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index 6fd9d9caee..3363084d1d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -87,7 +87,7 @@ private func filterOriginalMessageFlags(_ message: Message) -> Message { private func filterMessageChannelPeer(_ peer: Peer) -> Peer { if let peer = peer as? TelegramChannel { - return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus, approximateBoostLevel: peer.approximateBoostLevel, subscriptionUntilDate: peer.subscriptionUntilDate, verificationIconFileId: peer.verificationIconFileId, sendPaidMessageStars: peer.sendPaidMessageStars, linkedMonoforumId: peer.linkedMonoforumId) + return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus, approximateBoostLevel: peer.approximateBoostLevel, subscriptionUntilDate: peer.subscriptionUntilDate, verificationIconFileId: peer.verificationIconFileId, sendPaidMessageStars: peer.sendPaidMessageStars, linkedMonoforumId: peer.linkedMonoforumId, linkedBotId: peer.linkedBotId) } return peer } diff --git a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift index 2a2bea029b..88bf9e57a3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift @@ -522,7 +522,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess accountPeer: nil ) - let entryAttributes = ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil) + let entryAttributes = ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false) let items = self.messages.map { message -> ChatMessageBubbleContentItem in return ChatMessageBubbleContentItem( diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index efb36b911a..46503735dc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -1426,8 +1426,8 @@ private final class ChatSendStarsScreenComponent: Component { self.environment?.controller()?.dismiss() let _ = (context.engine.payments.starsTopUpOptions() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { options in + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { options in let controller = context.sharedContext.makeStarsPurchaseScreen( context: context, starsContext: starsContext, diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift index 1abefd9b5c..e9c96f72d5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift @@ -45,12 +45,18 @@ public final class ChatSideTopicsPanel: Component { case top } + public enum Kind { + case forum + case monoforum + case botForum + } + let context: AccountContext let theme: PresentationTheme let strings: PresentationStrings let location: Location let peerId: EnginePeer.Id - let isMonoforum: Bool + let kind: Kind let topicId: Int64? let controller: () -> ViewController? let togglePanel: () -> Void @@ -63,7 +69,7 @@ public final class ChatSideTopicsPanel: Component { strings: PresentationStrings, location: Location, peerId: EnginePeer.Id, - isMonoforum: Bool, + kind: Kind, topicId: Int64?, controller: @escaping () -> ViewController?, togglePanel: @escaping () -> Void, @@ -75,7 +81,7 @@ public final class ChatSideTopicsPanel: Component { self.strings = strings self.location = location self.peerId = peerId - self.isMonoforum = isMonoforum + self.kind = kind self.topicId = topicId self.controller = controller self.togglePanel = togglePanel @@ -99,7 +105,7 @@ public final class ChatSideTopicsPanel: Component { if lhs.peerId != rhs.peerId { return false } - if lhs.isMonoforum != rhs.isMonoforum { + if lhs.kind != rhs.kind { return false } if lhs.topicId != rhs.topicId { @@ -387,6 +393,7 @@ public final class ChatSideTopicsPanel: Component { avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize) } } else { + //newTopicTemplateIcon avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor) } } @@ -825,6 +832,7 @@ public final class ChatSideTopicsPanel: Component { avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize) } } else { + //newTopicTemplateIcon avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor) } } @@ -1144,12 +1152,14 @@ public final class ChatSideTopicsPanel: Component { private final class VerticalAllItemComponent: Component, AllItemComponent { let isSelected: Bool + let kind: ChatSideTopicsPanel.Kind let theme: PresentationTheme let strings: PresentationStrings let action: (() -> Void)? - init(isSelected: Bool, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) { + init(isSelected: Bool, kind: ChatSideTopicsPanel.Kind, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) { self.isSelected = isSelected + self.kind = kind self.theme = theme self.strings = strings self.action = action @@ -1162,6 +1172,9 @@ public final class ChatSideTopicsPanel: Component { if lhs.isSelected != rhs.isSelected { return false } + if lhs.kind != rhs.kind { + return false + } if lhs.theme !== rhs.theme { return false } @@ -1234,7 +1247,13 @@ public final class ChatSideTopicsPanel: Component { containerSize: CGSize(width: 100.0, height: 100.0) ) - let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab + let titleText: String + if case .botForum = component.kind { + //TODO:localize + titleText = "New Chat" + } else { + titleText = component.strings.Chat_InlineTopicMenu_AllTab + } let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( @@ -1284,12 +1303,14 @@ public final class ChatSideTopicsPanel: Component { private final class HorizontalAllItemComponent: Component, AllItemComponent { let isSelected: Bool + let kind: ChatSideTopicsPanel.Kind let theme: PresentationTheme let strings: PresentationStrings let action: (() -> Void)? - init(isSelected: Bool, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) { + init(isSelected: Bool, kind: ChatSideTopicsPanel.Kind, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) { self.isSelected = isSelected + self.kind = kind self.theme = theme self.strings = strings self.action = action @@ -1302,6 +1323,9 @@ public final class ChatSideTopicsPanel: Component { if lhs.isSelected != rhs.isSelected { return false } + if lhs.kind != rhs.kind { + return false + } if lhs.theme !== rhs.theme { return false } @@ -1361,7 +1385,13 @@ public final class ChatSideTopicsPanel: Component { let leftInset: CGFloat = 6.0 let rightInset: CGFloat = 12.0 - let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab + let titleText: String + if case .botForum = component.kind { + //TODO:localize + titleText = "New Chat" + } else { + titleText = component.strings.Chat_InlineTopicMenu_AllTab + } let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( @@ -1372,7 +1402,7 @@ public final class ChatSideTopicsPanel: Component { containerSize: CGSize(width: 400.0, height: 200.0) ) - let contentSize: CGFloat = leftInset + rightInset + titleSize.height + let contentSize: CGFloat = leftInset + rightInset + titleSize.width let size = CGSize(width: contentSize, height: availableSize.height) let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize) @@ -1422,6 +1452,7 @@ public final class ChatSideTopicsPanel: Component { private var tabItemView: TabItemView? + private var peerId: EnginePeer.Id? private var rawItems: [Item] = [] private var reorderingItems: [Item]? private var resetReorderingOnNextUpdate: Bool = false @@ -1653,14 +1684,55 @@ public final class ChatSideTopicsPanel: Component { } if self.component == nil { - let threadListSignal: Signal = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.isMonoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId)) + let threadListSignal: Signal<(EnginePeer.Id, EngineChatList), NoError> + + switch component.kind { + case .botForum: + let forumPeerId: Signal + if component.peerId.namespace == Namespaces.Peer.CloudUser { + forumPeerId = component.context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: component.peerId) + ) + |> map { value -> EnginePeer.Id? in + if case let .known(value) = value { + return value + } else { + return nil + } + } + |> distinctUntilChanged + } else { + forumPeerId = .single(component.peerId) + } + + let defaultPeerId = component.peerId + threadListSignal = forumPeerId + |> mapToSignal { forumPeerId -> Signal<(EnginePeer.Id, EngineChatList), NoError> in + if let forumPeerId { + return component.context.sharedContext.subscribeChatListData(context: component.context, location: .forum(peerId: forumPeerId)) + |> map { value in + return (forumPeerId, value) + } + } else { + return .single((defaultPeerId, EngineChatList(items: [], groupItems: [], additionalItems: [], hasEarlier: false, hasLater: false, isLoading: false))) + } + } + default: + let defaultPeerId = component.peerId + threadListSignal = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.kind == .monoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId)) + |> map { value in + return (defaultPeerId, value) + } + } self.itemsDisposable = (threadListSignal - |> deliverOnMainQueue).startStrict(next: { [weak self] chatList in - guard let self else { + |> deliverOnMainQueue).startStrict(next: { [weak self] peerId, chatList in + guard let self, let _ = self.component else { return } + self.peerId = peerId + let wasEmpty = self.rawItems.isEmpty self.rawItems.removeAll() @@ -1786,7 +1858,7 @@ public final class ChatSideTopicsPanel: Component { itemTransition = .immediate animateIn = true itemView = TabItemView(context: component.context, action: { [weak self] in - guard let self, let component = self.component else { + guard let self, let peerId = self.peerId, let component = self.component else { return } if self.isReordering { @@ -1806,7 +1878,7 @@ public final class ChatSideTopicsPanel: Component { } if threadIds != currentThreadIds { - let _ = component.context.engine.peers.setForumChannelPinnedTopics(id: component.peerId, threadIds: threadIds).startStandalone() + let _ = component.context.engine.peers.setForumChannelPinnedTopics(id: peerId, threadIds: threadIds).startStandalone() self.resetReorderingOnNextUpdate = true } else { self.reorderingItems = nil @@ -1880,6 +1952,7 @@ public final class ChatSideTopicsPanel: Component { id: ScrollId.all, component: AnyComponent(VerticalAllItemComponent( isSelected: component.topicId == nil, + kind: component.kind, theme: component.theme, strings: component.strings, action: { [weak self] in @@ -1895,6 +1968,7 @@ public final class ChatSideTopicsPanel: Component { id: ScrollId.all, component: AnyComponent(HorizontalAllItemComponent( isSelected: component.topicId == nil, + kind: component.kind, theme: component.theme, strings: component.strings, action: { [weak self] in @@ -1937,10 +2011,10 @@ public final class ChatSideTopicsPanel: Component { component.updateTopicId(topicId, direction) } var itemContextGesture: ((ContextGesture, ContextExtractedContentContainingNode) -> Void)? - if !self.isReordering && component.isMonoforum { + if !self.isReordering, case .monoforum = component.kind { itemContextGesture = { [weak self] gesture, sourceNode in Task { @MainActor in - guard let self, let component = self.component else { + guard let self, let peerId = self.peerId, let component = self.component else { return } guard let controller = component.controller() else { @@ -1964,18 +2038,18 @@ public final class ChatSideTopicsPanel: Component { var items: [ContextMenuItem] = [] let threadInfo = await component.context.engine.data.get( - TelegramEngine.EngineData.Item.Messages.ThreadInfo(peerId: component.peerId, threadId: topicId) + TelegramEngine.EngineData.Item.Messages.ThreadInfo(peerId: peerId, threadId: topicId) ).get() if let threadInfo, threadInfo.isMessageFeeRemoved { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ReinstatePaidMessages, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - guard let self, let component = self.component else { + guard let self, let peerId = self.peerId, let component = self.component else { return } c?.dismiss(completion: {}) - let _ = component.context.engine.peers.reinstateNoPaidMessagesException(scopePeerId: component.peerId, peerId: EnginePeer.Id(topicId)).startStandalone() + let _ = component.context.engine.peers.reinstateNoPaidMessagesException(scopePeerId: peerId, peerId: EnginePeer.Id(topicId)).startStandalone() }))) } @@ -2011,7 +2085,7 @@ public final class ChatSideTopicsPanel: Component { } } else if !self.isReordering { itemContextGesture = { [weak self] gesture, sourceNode in - guard let self, let component = self.component else { + guard let self, let peerId = self.peerId, let component = self.component else { return } guard let controller = component.controller() else { @@ -2043,7 +2117,7 @@ public final class ChatSideTopicsPanel: Component { let _ = (chatForumTopicMenuItems( context: component.context, - peerId: component.peerId, + peerId: peerId, threadId: topicId, isPinned: isPinned, isClosed: isClosed, @@ -2052,12 +2126,12 @@ public final class ChatSideTopicsPanel: Component { canSelect: false, customEdit: { [weak self] contextController in contextController.dismiss(completion: { - guard let self, let component = self.component, let threadData else { + guard let self, let peerId = self.peerId, let component = self.component, let threadData else { return } let editController = component.context.sharedContext.makeEditForumTopicScreen( context: component.context, - peerId: component.peerId, + peerId: peerId, threadId: topicId, threadInfo: threadData.info, isHidden: threadData.isHidden @@ -2066,7 +2140,7 @@ public final class ChatSideTopicsPanel: Component { }) }, customPinUnpin: { [weak self] contextController in - guard let self, let component = self.component else { + guard let self, let peerId = self.peerId, let component = self.component else { contextController.dismiss(completion: {}) return } @@ -2074,7 +2148,7 @@ public final class ChatSideTopicsPanel: Component { self.isTogglingPinnedItem = true self.dismissContextControllerOnNextUpdate = contextController - let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: component.peerId, threadId: topicId) + let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: topicId) |> deliverOnMainQueue).startStandalone(error: { [weak self, weak contextController] error in guard let self, let component = self.component else { contextController?.dismiss(completion: {}) @@ -2099,6 +2173,12 @@ public final class ChatSideTopicsPanel: Component { return } self.updateIsReordering(isReordering: true) + }, + onDeleted: { [weak self] in + guard let self, let component = self.component else { + return + } + component.updateTopicId(nil, false) } ) |> take(1) diff --git a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift index c8d1b2c137..c9bab92dc5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift @@ -16,7 +16,6 @@ import ChatMessageBubbleContentNode import CountrySelectionUI import TelegramStringFormatting import MergedAvatarsNode -import ChatControllerInteraction import TextNodeWithEntities public final class ChatUserInfoItem: ListViewItem { diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/BUILD b/submodules/TelegramUI/Components/ChatThemeScreen/BUILD new file mode 100644 index 0000000000..783bf6048c --- /dev/null +++ b/submodules/TelegramUI/Components/ChatThemeScreen/BUILD @@ -0,0 +1,44 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatThemeScreen", + module_name = "ChatThemeScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/PresentationDataUtils", + "//submodules/TelegramNotices", + "//submodules/AnimationUI", + "//submodules/MergeLists", + "//submodules/MediaResources", + "//submodules/StickerResources", + "//submodules/WallpaperResources", + "//submodules/TooltipUI", + "//submodules/SolidRoundedButtonNode", + "//submodules/AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode", + "//submodules/ShimmerEffect", + "//submodules/AttachmentUI", + "//submodules/AvatarNode", + "//submodules/Markdown", + "//submodules/AppBundle", + "//submodules/ActivityIndicator", + "//submodules/TelegramUI/Components/Gifts/GiftItemComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift similarity index 79% rename from submodules/TelegramUI/Sources/ChatThemeScreen.swift rename to submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift index cdf9b92f22..23fe009606 100644 --- a/submodules/TelegramUI/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift @@ -21,33 +21,37 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import ShimmerEffect import AttachmentUI +import AvatarNode private struct ThemeSettingsThemeEntry: Comparable, Identifiable { let index: Int - let emoticon: String? + let chatTheme: ChatTheme? let emojiFile: TelegramMediaFile? let themeReference: PresentationThemeReference? + let peer: EnginePeer? let nightMode: Bool var selected: Bool let theme: PresentationTheme let strings: PresentationStrings let wallpaper: TelegramWallpaper? - var stableId: Int { - return index + var stableId: String { + return self.chatTheme?.id ?? "\(self.index)" } static func ==(lhs: ThemeSettingsThemeEntry, rhs: ThemeSettingsThemeEntry) -> Bool { if lhs.index != rhs.index { return false } - if lhs.emoticon != rhs.emoticon { + if lhs.chatTheme != rhs.chatTheme { return false } - if lhs.themeReference?.index != rhs.themeReference?.index { return false } + if lhs.peer != rhs.peer { + return false + } if lhs.nightMode != rhs.nightMode { return false } @@ -70,29 +74,42 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, action: @escaping (String?) -> Void) -> ListViewItem { - return ThemeSettingsThemeIconItem(context: context, emoticon: self.emoticon, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action) + func item(context: AccountContext, action: @escaping (ChatTheme?) -> Void) -> ListViewItem { + return ThemeSettingsThemeIconItem(context: context, chatTheme: self.chatTheme, emojiFile: self.emojiFile, themeReference: self.themeReference, peer: self.peer, nightMode: self.nightMode, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action) } } - private class ThemeSettingsThemeIconItem: ListViewItem { let context: AccountContext - let emoticon: String? + let chatTheme: ChatTheme? let emojiFile: TelegramMediaFile? let themeReference: PresentationThemeReference? + let peer: EnginePeer? let nightMode: Bool let selected: Bool let theme: PresentationTheme let strings: PresentationStrings let wallpaper: TelegramWallpaper? - let action: (String?) -> Void + let action: (ChatTheme?) -> Void - public init(context: AccountContext, emoticon: String?, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference?, nightMode: Bool, selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (String?) -> Void) { + public init( + context: AccountContext, + chatTheme: ChatTheme?, + emojiFile: TelegramMediaFile?, + themeReference: PresentationThemeReference?, + peer: EnginePeer?, + nightMode: Bool, + selected: Bool, + theme: PresentationTheme, + strings: PresentationStrings, + wallpaper: TelegramWallpaper?, + action: @escaping (ChatTheme?) -> Void + ) { self.context = context - self.emoticon = emoticon + self.chatTheme = chatTheme self.emojiFile = emojiFile self.themeReference = themeReference + self.peer = peer self.nightMode = nightMode self.selected = selected self.theme = theme @@ -137,7 +154,7 @@ private class ThemeSettingsThemeIconItem: ListViewItem { public var selectable = true public func selected(listView: ListView) { - self.action(self.emoticon) + self.action(self.chatTheme) } } @@ -149,7 +166,7 @@ private struct ThemeSettingsThemeItemNodeTransition { let entries: [ThemeSettingsThemeEntry] } -private func ensureThemeVisible(listNode: ListView, emoticon: String?, animated: Bool) -> Bool { +private func ensureThemeVisible(listNode: ListView, themeId: String?, animated: Bool) -> Bool { var resultNode: ThemeSettingsThemeItemIconNode? var previousNode: ThemeSettingsThemeItemIconNode? var nextNode: ThemeSettingsThemeItemIconNode? @@ -158,7 +175,7 @@ private func ensureThemeVisible(listNode: ListView, emoticon: String?, animated: return } if resultNode == nil { - if node.item?.emoticon == emoticon { + if node.item?.chatTheme?.id == themeId { resultNode = node } else { previousNode = node @@ -183,7 +200,7 @@ private func ensureThemeVisible(listNode: ListView, emoticon: String?, animated: } } -private func preparedTransition(context: AccountContext, action: @escaping (String?) -> Void, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry], crossfade: Bool) -> ThemeSettingsThemeItemNodeTransition { +private func preparedTransition(context: AccountContext, action: @escaping (ChatTheme?) -> Void, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry], crossfade: Bool) -> ThemeSettingsThemeItemNodeTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } @@ -241,6 +258,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { private let emojiImageNode: TransformImageNode private var animatedStickerNode: AnimatedStickerNode? private var placeholderNode: StickerShimmerEffectNode + private var bubbleNode: ASImageNode? + private var avatarNode: AvatarNode? + private var replaceNode: ASImageNode? var snapshotView: UIView? var item: ThemeSettingsThemeIconItem? @@ -377,7 +397,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { var updatedSelected = false var updatedNightMode = false - if currentItem?.emoticon != item.emoticon { + if currentItem?.chatTheme?.id != item.chatTheme?.id { updatedEmoticon = true } if currentItem?.themeReference != item.themeReference { @@ -399,8 +419,13 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { let text = NSAttributedString(string: item.strings.Conversation_Theme_NoTheme, font: Font.semibold(15.0), textColor: item.theme.actionSheet.controlAccentColor) let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) - let emoticon = item.emoticon - let title = NSAttributedString(string: emoticon != nil ? "" : "❌", font: Font.regular(22.0), textColor: .black) + let emoticon: String + if let _ = item.chatTheme { + emoticon = "" + } else { + emoticon = "❌" + } + let title = NSAttributedString(string: emoticon, font: Font.regular(22.0), textColor: .black) let (_, emojiApply) = makeEmojiLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: 120.0, height: 90.0), insets: UIEdgeInsets()) @@ -430,7 +455,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { } strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((90.0 - textLayout.size.width) / 2.0), y: 24.0), size: textLayout.size) - strongSelf.textNode.isHidden = item.emoticon != nil + strongSelf.textNode.isHidden = emoticon.isEmpty strongSelf.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) strongSelf.containerNode.frame = CGRect(origin: CGPoint(x: 15.0, y: -15.0), size: CGSize(width: 90.0, height: 120.0)) @@ -485,6 +510,75 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { animatedStickerNode.frame = emojiFrame animatedStickerNode.updateLayout(size: emojiFrame.size) } + + if let _ = item.peer { + let bubbleNode: ASImageNode + if let current = strongSelf.bubbleNode { + bubbleNode = current + } else { + bubbleNode = ASImageNode() + strongSelf.insertSubnode(bubbleNode, belowSubnode: strongSelf.emojiContainerNode) + strongSelf.bubbleNode = bubbleNode + + var bubbleColor: UIColor? + if let theme = item.chatTheme, case let .gift(_, themeSettings) = theme { + if item.nightMode { + if let theme = themeSettings.first(where: { $0.baseTheme == .night || $0.baseTheme == .tinted }) { + let color = theme.wallpaper?.settings?.colors.first ?? theme.accentColor + bubbleColor = UIColor(rgb: UInt32(bitPattern: color)) + } + } else { + if let theme = themeSettings.first(where: { $0.baseTheme == .classic || $0.baseTheme == .day }) { + let color = theme.wallpaper?.settings?.colors.first ?? theme.accentColor + bubbleColor = UIColor(rgb: UInt32(bitPattern: color)) + } + } + } + if let bubbleColor { + bubbleNode.image = generateFilledRoundedRectImage(size: CGSize(width: 24.0, height: 48.0), cornerRadius: 12.0, color: bubbleColor) + } + } + bubbleNode.frame = CGRect(origin: CGPoint(x: 50.0, y: 12.0), size: CGSize(width: 24.0, height: 48.0)) + } else if let bubbleNode = strongSelf.bubbleNode { + strongSelf.bubbleNode = nil + bubbleNode.removeFromSupernode() + } + + if let peer = item.peer { + let avatarNode: AvatarNode + if let current = strongSelf.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0)) + strongSelf.insertSubnode(avatarNode, belowSubnode: strongSelf.emojiContainerNode) + strongSelf.avatarNode = avatarNode + avatarNode.setPeer(context: item.context, theme: item.theme, peer: peer, displayDimensions: CGSize(width: 20.0, height: 20.0)) + } + avatarNode.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0) + avatarNode.frame = CGRect(origin: CGPoint(x: 52.0, y: 14.0), size: CGSize(width: 20.0, height: 20.0)) + } else if let avatarNode = strongSelf.avatarNode { + strongSelf.avatarNode = nil + avatarNode.removeFromSupernode() + } + + if let _ = item.peer { + let replaceNode: ASImageNode + if let current = strongSelf.replaceNode { + replaceNode = current + } else { + replaceNode = ASImageNode() + strongSelf.insertSubnode(replaceNode, belowSubnode: strongSelf.emojiContainerNode) + strongSelf.replaceNode = replaceNode + replaceNode.image = generateTintedImage(image: UIImage(bundleImageName: "Settings/Refresh"), color: .white) + } + replaceNode.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0) + if let image = replaceNode.image { + replaceNode.frame = CGRect(origin: CGPoint(x: 53.0, y: 37.0), size: image.size) + } + } else if let replaceNode = strongSelf.replaceNode { + strongSelf.replaceNode = nil + replaceNode.removeFromSupernode() + } } }) } @@ -521,9 +615,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { } } -final class ChatThemeScreen: ViewController { - static let themeCrossfadeDuration: Double = 0.3 - static let themeCrossfadeDelay: Double = 0.25 +public final class ChatThemeScreen: ViewController { + public static let themeCrossfadeDuration: Double = 0.3 + public static let themeCrossfadeDelay: Double = 0.25 private var controllerNode: ChatThemeScreenNode { return self.displayNode as! ChatThemeScreenNode @@ -533,20 +627,20 @@ final class ChatThemeScreen: ViewController { private let context: AccountContext private let animatedEmojiStickers: [String: [StickerPackItem]] - private let initiallySelectedEmoticon: String? + private let initiallySelectedTheme: ChatTheme? private let peerName: String - let canResetWallpaper: Bool - private let previewTheme: (String?, Bool?) -> Void + fileprivate let canResetWallpaper: Bool + private let previewTheme: (ChatTheme?, Bool?) -> Void fileprivate let changeWallpaper: () -> Void fileprivate let resetWallpaper: () -> Void - private let completion: (String?) -> Void + private let completion: (ChatTheme?) -> Void private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - var dismissed: (() -> Void)? + public var dismissed: (() -> Void)? - var passthroughHitTestImpl: ((CGPoint) -> UIView?)? { + public var passthroughHitTestImpl: ((CGPoint) -> UIView?)? { didSet { if self.isNodeLoaded { self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl @@ -554,22 +648,22 @@ final class ChatThemeScreen: ViewController { } } - init( + public init( context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal), animatedEmojiStickers: [String: [StickerPackItem]], - initiallySelectedEmoticon: String?, + initiallySelectedTheme: ChatTheme?, peerName: String, canResetWallpaper: Bool, - previewTheme: @escaping (String?, Bool?) -> Void, + previewTheme: @escaping (ChatTheme?, Bool?) -> Void, changeWallpaper: @escaping () -> Void, resetWallpaper: @escaping () -> Void, - completion: @escaping (String?) -> Void + completion: @escaping (ChatTheme?) -> Void ) { self.context = context self.presentationData = updatedPresentationData.initial self.animatedEmojiStickers = animatedEmojiStickers - self.initiallySelectedEmoticon = initiallySelectedEmoticon + self.initiallySelectedTheme = initiallySelectedTheme self.peerName = peerName self.canResetWallpaper = canResetWallpaper self.previewTheme = previewTheme @@ -605,25 +699,25 @@ final class ChatThemeScreen: ViewController { } override public func loadDisplayNode() { - self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedEmoticon: self.initiallySelectedEmoticon, peerName: self.peerName) + self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedTheme: self.initiallySelectedTheme, peerName: self.peerName) self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl - self.controllerNode.previewTheme = { [weak self] emoticon, dark in + self.controllerNode.previewTheme = { [weak self] chatTheme, dark in guard let strongSelf = self else { return } - strongSelf.previewTheme((emoticon ?? ""), dark) + strongSelf.previewTheme((chatTheme ?? .emoticon("")), dark) } self.controllerNode.present = { [weak self] c in self?.present(c, in: .current) } - self.controllerNode.completion = { [weak self] emoticon in + self.controllerNode.completion = { [weak self] chatTheme in guard let strongSelf = self else { return } strongSelf.dismiss(animated: true) - if strongSelf.initiallySelectedEmoticon == nil && emoticon == nil { + if strongSelf.initiallySelectedTheme == nil && chatTheme == nil { } else { - strongSelf.completion(emoticon) + strongSelf.completion(chatTheme) } } self.controllerNode.dismiss = { [weak self] in @@ -653,7 +747,7 @@ final class ChatThemeScreen: ViewController { } } - override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + override public func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { self.forEachController({ controller in if let controller = controller as? TooltipScreen { controller.dismiss() @@ -679,7 +773,7 @@ final class ChatThemeScreen: ViewController { self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) } - func dimTapped() { + public func dimTapped() { self.controllerNode.dimTapped() } } @@ -738,15 +832,18 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega private var enqueuedTransitions: [ThemeSettingsThemeItemNodeTransition] = [] private var initialized = false + private let uniqueGiftChatThemesContext: UniqueGiftChatThemesContext + private var currentUniqueGiftChatThemesState: UniqueGiftChatThemesContext.State? + private let peerName: String - private let initiallySelectedEmoticon: String? - private var selectedEmoticon: String? { + private let initiallySelectedTheme: ChatTheme? + private var selectedTheme: ChatTheme? { didSet { - self.selectedEmoticonPromise.set(self.selectedEmoticon) + self.selectedThemePromise.set(self.selectedTheme) } } - private var selectedEmoticonPromise: ValuePromise + private var selectedThemePromise: ValuePromise private var isDarkAppearancePromise: ValuePromise private var isDarkAppearance: Bool = false { @@ -760,20 +857,22 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega private let disposable = MetaDisposable() var present: ((ViewController) -> Void)? - var previewTheme: ((String?, Bool?) -> Void)? - var completion: ((String?) -> Void)? + var previewTheme: ((ChatTheme?, Bool?) -> Void)? + var completion: ((ChatTheme?) -> Void)? var dismiss: (() -> Void)? var cancel: (() -> Void)? - init(context: AccountContext, presentationData: PresentationData, controller: ChatThemeScreen, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, peerName: String) { + init(context: AccountContext, presentationData: PresentationData, controller: ChatThemeScreen, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedTheme: ChatTheme?, peerName: String) { self.context = context self.controller = controller - self.initiallySelectedEmoticon = initiallySelectedEmoticon + self.initiallySelectedTheme = initiallySelectedTheme self.peerName = peerName - self.selectedEmoticon = initiallySelectedEmoticon - self.selectedEmoticonPromise = ValuePromise(initiallySelectedEmoticon) + self.selectedTheme = initiallySelectedTheme + self.selectedThemePromise = ValuePromise(initiallySelectedTheme) self.presentationData = presentationData + self.uniqueGiftChatThemesContext = UniqueGiftChatThemesContext(account: context.account) + self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode.view.alwaysBounceVertical = true self.wrappingScrollNode.view.delaysContentTouches = false @@ -867,9 +966,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega self.cancelButtonNode.buttonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) self.doneButton.pressed = { [weak self] in if let strongSelf = self { - strongSelf.doneButton.isUserInteractionEnabled = false if strongSelf.doneButton.font == .bold { - strongSelf.completion?(strongSelf.selectedEmoticon) + strongSelf.complete() } else { strongSelf.controller?.changeWallpaper() } @@ -877,33 +975,135 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega } self.otherButton.addTarget(self, action: #selector(self.otherButtonPressed), forControlEvents: .touchUpInside) - self.disposable.set(combineLatest(queue: Queue.mainQueue(), self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager), self.selectedEmoticonPromise.get(), self.isDarkAppearancePromise.get()).startStrict(next: { [weak self] themes, selectedEmoticon, isDarkAppearance in + self.disposable.set(combineLatest( + queue: Queue.mainQueue(), + self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager), + self.uniqueGiftChatThemesContext.state + |> mapToSignal { state -> Signal<(UniqueGiftChatThemesContext.State, [EnginePeer.Id: EnginePeer]), NoError> in + var peerIds: [EnginePeer.Id] = [] + for theme in state.themes { + if case let .gift(gift, _) = theme, case let .unique(uniqueGift) = gift, let themePeerId = uniqueGift.themePeerId { + peerIds.append(themePeerId) + } + } + return combineLatest( + .single(state), + context.engine.data.get( + EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)) + ) |> map { peers in + var result: [EnginePeer.Id: EnginePeer] = [:] + for peerId in peerIds { + if let maybePeer = peers[peerId], let peer = maybePeer { + result[peerId] = peer + } + } + return result + } + ) + }, + self.selectedThemePromise.get(), + self.isDarkAppearancePromise.get() + ).startStrict(next: { [weak self] themes, uniqueGiftChatThemesStateAndPeers, selectedTheme, isDarkAppearance in guard let strongSelf = self else { return } - - let selectedEmoticon = selectedEmoticon?.strippedEmoji - + let (uniqueGiftChatThemesState, peers) = uniqueGiftChatThemesStateAndPeers + strongSelf.currentUniqueGiftChatThemesState = uniqueGiftChatThemesState + let isFirstTime = strongSelf.entries == nil let presentationData = strongSelf.presentationData var entries: [ThemeSettingsThemeEntry] = [] - entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: nil, emojiFile: nil, themeReference: nil, nightMode: false, selected: selectedEmoticon == nil, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) - for theme in themes { - guard let emoticon = theme.emoticon else { - continue + entries.append(ThemeSettingsThemeEntry( + index: 0, + chatTheme: nil, + emojiFile: nil, + themeReference: nil, + peer: nil, + nightMode: false, + selected: selectedTheme == nil, + theme: presentationData.theme, + strings: presentationData.strings, + wallpaper: nil + )) + + var giftThemes = uniqueGiftChatThemesState.themes + var existingIds = Set() + if let initiallySelectedTheme, case .gift = initiallySelectedTheme { + let initialThemeIndex = giftThemes.firstIndex(where: { $0.id == initiallySelectedTheme.id }) + if initialThemeIndex == nil || initialThemeIndex! > 50 { + giftThemes.insert(initiallySelectedTheme, at: 0) + existingIds.insert(initiallySelectedTheme.id) } - entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: emoticon, emojiFile: animatedEmojiStickers[emoticon]?.first?.file._parse(), themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)), nightMode: isDarkAppearance, selected: selectedEmoticon == theme.emoticon?.strippedEmoji, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) } - let action: (String?) -> Void = { [weak self] emoticon in - if let strongSelf = self, strongSelf.selectedEmoticon != emoticon { - strongSelf.setEmoticon(emoticon) + for theme in giftThemes { + guard case let .gift(gift, themeSettings) = theme, !existingIds.contains(theme.id) else { + continue + } + var emojiFile: TelegramMediaFile? + var peer: EnginePeer? + if case let .unique(uniqueGift) = gift { + for attribute in uniqueGift.attributes { + if case let .model(_, file, _) = attribute { + emojiFile = file + } + } + if let themePeerId = uniqueGift.themePeerId, theme.id != initiallySelectedTheme?.id { + peer = peers[themePeerId] + } + } + let themeReference: PresentationThemeReference + let wallpaper: TelegramWallpaper? + if isDarkAppearance { + wallpaper = themeSettings.first(where: { $0.baseTheme == .night || $0.baseTheme == .tinted })?.wallpaper + themeReference = .builtin(.night) + } else { + wallpaper = themeSettings.first(where: { $0.baseTheme == .classic || $0.baseTheme == .day })?.wallpaper + themeReference = .builtin(.dayClassic) + } + entries.append(ThemeSettingsThemeEntry( + index: entries.count, + chatTheme: theme, + emojiFile: emojiFile, + themeReference: themeReference, + peer: peer, + nightMode: isDarkAppearance, + selected: selectedTheme?.id == theme.id, + theme: presentationData.theme, + strings: presentationData.strings, + wallpaper: wallpaper + )) + } + + if uniqueGiftChatThemesState.themes.count == 0 || uniqueGiftChatThemesState.dataState == .ready(canLoadMore: false) { + for theme in themes { + guard let emoticon = theme.emoticon else { + continue + } + entries.append(ThemeSettingsThemeEntry( + index: entries.count, + chatTheme: .emoticon(emoticon), + emojiFile: animatedEmojiStickers[emoticon]?.first?.file._parse(), + themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)), + peer: nil, + nightMode: isDarkAppearance, + selected: selectedTheme?.id == ChatTheme.emoticon(emoticon).id, + theme: presentationData.theme, + strings: presentationData.strings, + wallpaper: nil + )) + } + } + + let action: (ChatTheme?) -> Void = { [weak self] chatTheme in + if let self, self.selectedTheme != chatTheme { + self.setChatTheme(chatTheme) } } let previousEntries = strongSelf.entries ?? [] - let crossfade = previousEntries.count != entries.count - let transition = preparedTransition(context: strongSelf.context, action: action, from: previousEntries, to: entries, crossfade: crossfade) + //let crossfade = previousEntries.count != entries.count + let transition = preparedTransition(context: strongSelf.context, action: action, from: previousEntries, to: entries, crossfade: false) strongSelf.enqueueTransition(transition) strongSelf.entries = entries @@ -952,6 +1152,15 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega } } + self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in + guard let self, let state = self.currentUniqueGiftChatThemesState, case .ready(true) = state.dataState else { + return + } + if case let .known(value) = offset, value < 100.0 { + self.uniqueGiftChatThemesContext.loadMore() + } + } + self.updateCancelButton() } @@ -982,7 +1191,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega var scrollToItem: ListViewScrollToItem? if !self.initialized { if let index = transition.entries.firstIndex(where: { entry in - return entry.emoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji + return entry.chatTheme?.id == self.initiallySelectedTheme?.id }) { scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down) self.initialized = true @@ -994,13 +1203,13 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega } private var skipButtonsUpdate = false - private func setEmoticon(_ emoticon: String?) { + private func setChatTheme(_ chatTheme: ChatTheme?) { self.animateCrossfade(animateIcon: true) self.skipButtonsUpdate = true - self.previewTheme?(emoticon, self.isDarkAppearance) - self.selectedEmoticon = emoticon - let _ = ensureThemeVisible(listNode: self.listNode, emoticon: emoticon, animated: true) + self.previewTheme?(chatTheme, self.isDarkAppearance) + self.selectedTheme = chatTheme + let _ = ensureThemeVisible(listNode: self.listNode, themeId: chatTheme?.id, animated: true) UIView.transition(with: self.buttonsContentContainerNode.view, duration: ChatThemeScreen.themeCrossfadeDuration, options: [.transitionCrossDissolve, .curveLinear]) { self.updateButtons() @@ -1018,11 +1227,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega let doneButtonTitle: String var accentButtonTheme = true var otherIsEnabled = false - if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { + if self.selectedTheme?.id == self.initiallySelectedTheme?.id { otherIsEnabled = self.controller?.canResetWallpaper == true doneButtonTitle = otherIsEnabled ? self.presentationData.strings.Conversation_Theme_SetNewPhotoWallpaper : self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper accentButtonTheme = false - } else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil { + } else if self.selectedTheme?.id == nil && self.initiallySelectedTheme?.id != nil { doneButtonTitle = self.presentationData.strings.Conversation_Theme_Reset } else { doneButtonTitle = self.presentationData.strings.Conversation_Theme_Apply @@ -1051,9 +1260,9 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega private func updateCancelButton() { var cancelButtonState: WebAppCancelButtonNode.State = .cancel - if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { + if self.selectedTheme?.id == self.initiallySelectedTheme?.id { - } else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil { + } else if self.selectedTheme == nil && self.initiallySelectedTheme != nil { cancelButtonState = .back } else { cancelButtonState = .back @@ -1120,15 +1329,15 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega @objc func cancelButtonPressed() { if self.cancelButtonNode.state == .back { - self.setEmoticon(self.initiallySelectedEmoticon) + self.setChatTheme(self.initiallySelectedTheme) } else { self.cancel?() } } @objc func otherButtonPressed() { - if self.selectedEmoticon?.strippedEmoji != self.initiallySelectedEmoticon?.strippedEmoji { - self.setEmoticon(self.initiallySelectedEmoticon) + if self.selectedTheme?.id != self.initiallySelectedTheme?.id { + self.setChatTheme(self.initiallySelectedTheme) } else { if self.controller?.canResetWallpaper == true { self.controller?.resetWallpaper() @@ -1139,13 +1348,39 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega } } + func complete() { + let proceed = { + self.doneButton.isUserInteractionEnabled = false + self.completion?(self.selectedTheme) + } + if case let .gift(gift, _) = self.selectedTheme, case let .unique(uniqueGift) = gift, let themePeerId = uniqueGift.themePeerId { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: themePeerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + let controller = giftThemeTransferAlertController( + context: self.context, + gift: uniqueGift, + previousPeer: peer, + commit: { + proceed() + } + ) + self.controller?.present(controller, in: .window(.root)) + }) + } else { + proceed() + } + } + func dimTapped() { - if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { + if self.selectedTheme?.id == self.initiallySelectedTheme?.id { self.cancelButtonPressed() } else { let alertController = textAlertController(context: self.context, updatedPresentationData: (self.presentationData, .single(self.presentationData)), title: nil, text: self.presentationData.strings.Conversation_Theme_DismissAlert, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_Theme_DismissAlertApply, action: { [weak self] in - if let strongSelf = self { - strongSelf.completion?(strongSelf.selectedEmoticon) + if let self { + self.complete() } })], actionLayout: .horizontal, dismissOnOutsideTap: true) self.present?(alertController) @@ -1165,7 +1400,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega } let isDarkAppearance = !self.isDarkAppearance - self.previewTheme?(self.selectedEmoticon, isDarkAppearance) + self.previewTheme?(self.selectedTheme, isDarkAppearance) self.isDarkAppearance = isDarkAppearance if isDarkAppearance { diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift new file mode 100644 index 0000000000..03ea78002f --- /dev/null +++ b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift @@ -0,0 +1,295 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import AppBundle +import AvatarNode +import Markdown +import GiftItemComponent +import ActivityIndicator + +private final class GiftThemeTransferAlertContentNode: AlertContentNode { + private let context: AccountContext + private let strings: PresentationStrings + private var presentationTheme: PresentationTheme + private let title: String + private let text: String + private let gift: StarGift.UniqueGift + + private let titleNode: ASTextNode + private let giftView = ComponentView() + private let textNode: ASTextNode + private let arrowNode: ASImageNode + private let avatarNode: AvatarNode + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [TextAlertContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private var activityIndicator: ActivityIndicator? + + private var validLayout: CGSize? + + var inProgress = false { + didSet { + if let size = self.validLayout { + let _ = self.updateLayout(size: size, transition: .immediate) + } + } + } + + override var dismissOnOutsideTap: Bool { + return self.isUserInteractionEnabled + } + + init( + context: AccountContext, + theme: AlertControllerTheme, + ptheme: PresentationTheme, + strings: PresentationStrings, + gift: StarGift.UniqueGift, + peer: EnginePeer, + title: String, + text: String, + actions: [TextAlertAction] + ) { + self.context = context + self.strings = strings + self.presentationTheme = ptheme + self.title = title + self.text = text + self.gift = gift + + self.titleNode = ASTextNode() + self.titleNode.maximumNumberOfLines = 0 + + self.textNode = ASTextNode() + self.textNode.maximumNumberOfLines = 0 + + self.arrowNode = ASImageNode() + self.arrowNode.displaysAsynchronously = false + self.arrowNode.displayWithoutProcessing = true + + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + + self.actionNodes = actions.map { action -> TextAlertContentActionNode in + return TextAlertContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.textNode) + self.addSubnode(self.arrowNode) + self.addSubnode(self.avatarNode) + + self.addSubnode(self.actionNodesSeparator) + + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + } + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + + self.updateTheme(theme) + + self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) + self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( + body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), + bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), + link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), + linkAttribute: { url in + return ("URL", url) + } + ), textAlignment: .center) + self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/CutoutUndo"), color: theme.secondaryColor.withAlphaComponent(0.9)) + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + var size = size + size.width = min(size.width, 270.0) + + self.validLayout = size + + var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) + + let avatarSize = CGSize(width: 60.0, height: 60.0) + self.avatarNode.updateSize(size: avatarSize) + + let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 52.0, y: origin.y), size: avatarSize) + + let _ = self.giftView.update( + transition: .immediate, + component: AnyComponent( + GiftItemComponent( + context: self.context, + theme: self.presentationTheme, + strings: self.strings, + peer: nil, + subject: .uniqueGift(gift: self.gift, price: nil), + mode: .thumbnail + ) + ), + environment: {}, + containerSize: avatarSize + ) + if let view = self.giftView.view { + if view.superview == nil { + self.view.addSubview(view) + } + view.frame = giftFrame + } + + if let arrowImage = self.arrowNode.image { + let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + transition.updateFrame(node: self.arrowNode, frame: arrowFrame) + } + + let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 52.0, y: origin.y), size: avatarSize) + transition.updateFrame(node: self.avatarNode, frame: avatarFrame) + + origin.y += avatarSize.height + 17.0 + + let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) + origin.y += titleSize.height + 5.0 + + let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) + origin.y += textSize.height + 10.0 + + let actionButtonHeight: CGFloat = 44.0 + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) + minActionsWidth += actionTitleSize.width + actionTitleInsets + } + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) + + let contentWidth = max(size.width, minActionsWidth) + + let actionsHeight = actionButtonHeight + + let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 24.0 + insets.top + insets.bottom) + self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + + let actionNodeFrame: CGRect + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + if self.inProgress { + let activityIndicator: ActivityIndicator + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) + self.addSubnode(activityIndicator) + } + + if let actionNode = self.actionNodes.first { + actionNode.isHidden = true + + let indicatorSize = CGSize(width: 22.0, height: 22.0) + transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) + } + } + + return resultSize + } +} + +public func giftThemeTransferAlertController( + context: AccountContext, + gift: StarGift.UniqueGift, + previousPeer: EnginePeer, + commit: @escaping () -> Void +) -> AlertController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + var contentNode: GiftThemeTransferAlertContentNode? + var dismissImpl: ((Bool) -> Void)? + let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?(true) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Conversation_Theme_GiftTransfer_Proceed, action: { + dismissImpl?(true) + commit() + })] + + let text = strings.Conversation_Theme_GiftTransfer_Text(previousPeer.compactDisplayTitle).string + contentNode = GiftThemeTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: previousPeer, title: "", text: text, actions: actions) + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) + dismissImpl = { [weak controller] animated in + if animated { + controller?.dismissAnimated() + } else { + controller?.dismiss() + } + } + return controller +} diff --git a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/BUILD b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/BUILD index ab8dc02d74..9579870f15 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/BUILD @@ -28,6 +28,7 @@ swift_library( "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode", + "//submodules/UIKitRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift index 2f3eb20f16..78fe8a36ad 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift @@ -13,6 +13,7 @@ import PeerInfoCoverComponent import AnimatedStickerNode import TelegramAnimatedStickerNode import EmojiStatusComponent +import UIKitRuntimeUtils public final class GiftCompositionComponent: Component { public class ExternalState { @@ -24,7 +25,7 @@ public final class GiftCompositionComponent: Component { public enum Subject: Equatable { case generic(TelegramMediaFile) - case unique(StarGift.UniqueGift) + case unique([StarGift.UniqueGift.Attribute]?, StarGift.UniqueGift) case preview([StarGift.UniqueGift.Attribute]) } @@ -34,8 +35,9 @@ public final class GiftCompositionComponent: Component { let animationOffset: CGPoint? let animationScale: CGFloat? let displayAnimationStars: Bool + let revealedAttributes: Set let externalState: ExternalState? - let requestUpdate: () -> Void + let requestUpdate: (ComponentTransition) -> Void public init( context: AccountContext, @@ -44,8 +46,9 @@ public final class GiftCompositionComponent: Component { animationOffset: CGPoint? = nil, animationScale: CGFloat? = nil, displayAnimationStars: Bool = false, + revealedAttributes: Set = Set(), externalState: ExternalState? = nil, - requestUpdate: @escaping () -> Void = {} + requestUpdate: @escaping (ComponentTransition) -> Void = { _ in } ) { self.context = context self.theme = theme @@ -53,6 +56,7 @@ public final class GiftCompositionComponent: Component { self.animationOffset = animationOffset self.animationScale = animationScale self.displayAnimationStars = displayAnimationStars + self.revealedAttributes = revealedAttributes self.externalState = externalState self.requestUpdate = requestUpdate } @@ -76,6 +80,9 @@ public final class GiftCompositionComponent: Component { if lhs.displayAnimationStars != rhs.displayAnimationStars { return false } + if lhs.revealedAttributes != rhs.revealedAttributes { + return false + } return true } @@ -102,10 +109,43 @@ public final class GiftCompositionComponent: Component { private var previewBackdropIndex: Int32 = 0 private var previewPatternIndex: Int32 = 0 private var animatePreviewTransition = false + private var animateBackdropSwipe = false + private enum SpinState { case idle, spinning, decelerating, settled } + private var spinState: SpinState = .idle + private var spinLink: SharedDisplayLinkDriver.Link? + private var lastSpawnTime: CFTimeInterval? + private var lastPatternChangeTime: CFTimeInterval? + private var lastBackdropChangeTime: CFTimeInterval? + private var currentInterval: Double = 0.0 + + private var deceleraionQueue: [StarGift.UniqueGift.Attribute] = [] + private var decelerationTotalSteps: Int = 0 + private var decelerationStepIndex: Int = 0 + private var decelContainer: UIView? + private var decelItemHosts: [UIView] = [] + private let decelAnimationKey = "decel.container.move" + + private var activeWrappers: [UIView] = [] + + private struct SpinGeom { + var availableSize: CGSize + var iconSize: CGSize + var scale: CGFloat + var centerX: CGFloat + var centerY: CGFloat + } + private var spinGeom: SpinGeom? + + private var spinPool: [StarGift.UniqueGift.Attribute] = [] + private var spinPoolIndex: Int = 0 + + private let baseAnimDuration: Double = 0.4 + private let maxAnimDuration: Double = 1.3 + private let spacingX: CGFloat = 50.0 + override init(frame: CGRect) { super.init(frame: frame) - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap))) } @@ -115,17 +155,443 @@ public final class GiftCompositionComponent: Component { deinit { self.disposables.dispose() + self.previewTimer?.invalidate() } @objc private func handleTap() { - guard let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl else { - return - } + guard let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl else { return } if case .once = animationNode.playbackMode, !animationNode.isPlaying { animationNode.playOnce() } } + private func stopSpinIfNeeded() { + self.spinState = .idle + self.spinLink?.invalidate() + self.spinLink = nil + self.lastSpawnTime = nil + self.currentInterval = 0.0 + self.deceleraionQueue.removeAll() + self.decelerationTotalSteps = 0 + self.decelerationStepIndex = 0 + self.spinPool.removeAll() + self.spinPoolIndex = 0 + self.spinGeom = nil + + for wrapper in self.activeWrappers { + wrapper.layer.removeAllAnimations() + wrapper.removeFromSuperview() + } + self.activeWrappers.removeAll() + + if let c = self.decelContainer { + c.layer.removeAllAnimations() + c.removeFromSuperview() + } + self.decelContainer = nil + self.decelItemHosts.removeAll() + } + + private func ensureDisplayLink() { + if self.spinLink != nil { return } + self.spinLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] _ in + self?.tick() + }) + } + + private func spawnModelItem( + _ attribute: StarGift.UniqueGift.Attribute, + animDuration: Double + ) { + guard let geom = self.spinGeom, case let .model(_, file, _) = attribute else { + return + } + + let node = DefaultAnimatedStickerNodeImpl() + node.isUserInteractionEnabled = false + let pathPrefix = self.component!.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) + node.setup( + source: AnimatedStickerResourceSource(account: self.component!.context.account, resource: file.resource, isVideo: file.isVideoSticker), + width: Int(geom.iconSize.width * 1.6), + height: Int(geom.iconSize.height * 1.6), + playbackMode: .still(.start), + mode: .direct(cachePathPrefix: pathPrefix) + ) + node.updateLayout(size: geom.iconSize) + + let scaleValue = geom.scale + let visualSize = CGSize(width: geom.iconSize.width * scaleValue, height: geom.iconSize.height * scaleValue) + let wrapper = UIView(frame: CGRect(origin: .zero, size: visualSize)) + wrapper.clipsToBounds = false + + let host = node.view + host.frame = CGRect(origin: .zero, size: geom.iconSize) + host.layer.bounds = CGRect(origin: .zero, size: geom.iconSize) + host.layer.position = CGPoint(x: geom.iconSize.width / 2.0, y: geom.iconSize.height / 2.0) + host.layer.transform = CATransform3DMakeScale(scaleValue, scaleValue, 1.0) + wrapper.addSubview(host) + + self.addSubview(wrapper) + self.activeWrappers.append(wrapper) + + let centerY = geom.centerY - visualSize.height / 2.0 + let startX = -visualSize.width * 1.5 + let endX = geom.availableSize.width + visualSize.width + + wrapper.frame.origin = CGPoint(x: endX, y: centerY) + + let travelDistance = abs(startX - endX) + let pitch = visualSize.width + self.spacingX + + wrapper.layer.animatePosition( + from: CGPoint(x: -travelDistance, y: 0.0), + to: .zero, + duration: animDuration, + timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, + additive: true, + completion: { [weak self, weak wrapper] _ in + if let self, let w = wrapper { + self.activeWrappers.removeAll { $0 === w } + w.removeFromSuperview() + } + } + ) + + self.currentInterval = Double(pitch / travelDistance) * animDuration * 0.6 + } + + private func finishSettled() { + guard self.spinState != .settled else { return } + self.spinState = .settled + self.spinLink?.invalidate() + self.spinLink = nil + + for v in self.activeWrappers { + v.layer.removeAllAnimations() + v.removeFromSuperview() + } + self.activeWrappers.removeAll() + } + + private func startSpinningUnique( + availableSize: CGSize, + iconSize: CGSize, + scale: CGFloat, + pool: [StarGift.UniqueGift.Attribute] + ) { + self.stopSpinIfNeeded() + + self.spinPool = pool + self.spinPoolIndex = 0 + let centerY = 88.0 + (self.component?.animationOffset?.y ?? 0.0) + + self.spinGeom = SpinGeom( + availableSize: availableSize, + iconSize: iconSize, + scale: scale, + centerX: availableSize.width / 2.0 + (self.component?.animationOffset?.x ?? 0.0), + centerY: centerY + ) + + self.spinState = .spinning + self.lastSpawnTime = nil + self.currentInterval = 0 + self.ensureDisplayLink() + } + + private func beginDecelerationWithQueue( + tail: [StarGift.UniqueGift.Attribute], + availableSize: CGSize, + iconSize: CGSize, + scale: CGFloat + ) { + guard let geom = self.spinGeom, !tail.isEmpty else { return } + + let visualSize = CGSize(width: iconSize.width * scale, height: iconSize.height * scale) + let pitch = visualSize.width + self.spacingX + let count = tail.count + let containerWidth = CGFloat(count) * visualSize.width + CGFloat(max(count - 1, 0)) * self.spacingX + let containerHeight = visualSize.height + + let container = UIView(frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: containerHeight))) + container.isUserInteractionEnabled = false + container.clipsToBounds = false + + let containerY = geom.centerY - containerHeight / 2.0 + container.frame.origin.y = containerY + self.addSubview(container) + self.decelContainer = container + self.decelItemHosts.removeAll() + + for (i, attribute) in tail.reversed().enumerated() { + guard case let .model(_, file, _) = attribute else { continue } + + let node = DefaultAnimatedStickerNodeImpl() + node.isUserInteractionEnabled = false + let pathPrefix = self.component!.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) + node.setup( + source: AnimatedStickerResourceSource(account: self.component!.context.account, resource: file.resource, isVideo: file.isVideoSticker), + width: Int(iconSize.width * 1.6), + height: Int(iconSize.height * 1.6), + playbackMode: .still(.start), + mode: .direct(cachePathPrefix: pathPrefix) + ) + node.updateLayout(size: iconSize) + node.visibility = true + if i < 4 { + node.playOnce(); + } + + let host = node.view + host.bounds = CGRect(origin: .zero, size: iconSize) + host.layer.transform = CATransform3DMakeScale(scale, scale, 1.0) + + let hostView = UIView(frame: CGRect(origin: CGPoint(x: CGFloat(i) * pitch, y: 0), size: visualSize)) + host.center = CGPoint(x: visualSize.width / 2.0, y: visualSize.height / 2.0) + hostView.addSubview(host) + container.addSubview(hostView) + self.decelItemHosts.append(hostView) + + if i == 0 { + self.animationNode = node + + let factors: [CGFloat] = [1.0, 1.3, 0.92, 1.18, 0.98, 1.0] + let values = factors.map { NSNumber(value: Double($0)) } + let scaleAnim = CAKeyframeAnimation(keyPath: "transform.scale") + scaleAnim.beginTime = CACurrentMediaTime() + 0.6 + scaleAnim.values = values + scaleAnim.keyTimes = [0.0, 0.35, 0.55, 0.75, 0.9, 1.0].map(NSNumber.init) + scaleAnim.timingFunctions = [ + CAMediaTimingFunction(name: .easeOut), + CAMediaTimingFunction(name: .easeIn), + CAMediaTimingFunction(name: .easeOut), + CAMediaTimingFunction(name: .easeIn), + CAMediaTimingFunction(name: .easeOut) + ] + scaleAnim.duration = 0.85 + scaleAnim.isRemovedOnCompletion = true + host.layer.add(scaleAnim, forKey: "bounce") + } + + if i == 1 { + hostView.alpha = 0.0 + hostView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.7) + } + } + + container.frame.origin.x = floor((availableSize.width - visualSize.width) / 2.0) + + container.layer.animatePosition( + from: CGPoint(x: -containerWidth - visualSize.width * 0.5 + containerWidth / 2.0 - self.spacingX - 120.0, y: container.frame.center.y), + to: CGPoint(x: container.frame.center.x, y: container.frame.center.y), + duration: self.maxAnimDuration, + delay: 0.05, + timingFunction: kCAMediaTimingFunctionSpring, + completion: { [weak self] _ in + guard let self, let container = self.decelContainer else { + return + } + let _ = container + //self.handleDecelArrived(container: container, iconSize: iconSize, visualSize: visualSize) + } + ) + + self.spinState = .decelerating + self.ensureDisplayLink() + } + + private func handleDecelArrived(container: UIView, iconSize: CGSize, visualSize: CGSize) { + let isFinalIndex = self.decelItemHosts.count - 1 + guard isFinalIndex >= 0, let finalHostView = self.decelItemHosts.last else { return } + + guard let node = self.animationNode as? DefaultAnimatedStickerNodeImpl else { return } + node.playbackMode = .once + node.visibility = true + + let finalCenterInSelf = container.convert(finalHostView.center, to: self) + + let host = node.view + if host.superview !== self { self.addSubview(host) } + node.updateLayout(size: iconSize) + host.bounds = CGRect(origin: .zero, size: iconSize) + host.layer.position = finalCenterInSelf + + container.removeFromSuperview() + self.decelContainer = nil + self.decelItemHosts.removeAll() + + let factors: [CGFloat] = [1.0, 1.3, 0.92, 1.18, 0.98, 1.0] + let values = factors.map { NSNumber(value: Double($0)) } + let scaleAnim = CAKeyframeAnimation(keyPath: "transform.scale") + scaleAnim.values = values + scaleAnim.keyTimes = [0.0, 0.35, 0.55, 0.75, 0.9, 1.0].map(NSNumber.init) + scaleAnim.timingFunctions = [ + CAMediaTimingFunction(name: .easeOut), + CAMediaTimingFunction(name: .easeIn), + CAMediaTimingFunction(name: .easeOut), + CAMediaTimingFunction(name: .easeIn), + CAMediaTimingFunction(name: .easeOut) + ] + scaleAnim.duration = 0.85 + scaleAnim.isRemovedOnCompletion = true + host.layer.add(scaleAnim, forKey: "bounce") + + node.playOnce() + self.finishSettled() + } + + private func tick() { + guard let component = self.component else { return } + let now = CACurrentMediaTime() + + switch self.spinState { + case .spinning: + if self.lastSpawnTime == nil || now - (self.lastSpawnTime ?? now) >= self.currentInterval { + self.lastSpawnTime = now + + guard !self.spinPool.isEmpty else { return } + + if self.spinPoolIndex >= self.spinPool.count { self.spinPoolIndex = 0 } + let next = self.spinPool[self.spinPoolIndex] + self.spinPoolIndex += 1 + + self.spawnModelItem(next, animDuration: self.baseAnimDuration) + } + + var updateNeeded = false + if self.lastPatternChangeTime == nil || now - (self.lastPatternChangeTime ?? now) >= self.currentInterval * 6.0 { + self.lastPatternChangeTime = now + + if component.revealedAttributes.contains(.pattern) { + if self.previewPatternIndex != -1 { + self.previewPatternIndex = -1 + self.animatePreviewTransition = true + updateNeeded = true + } + } else { + let previousPatternIndex = self.previewPatternIndex + var randomPatternIndex = previousPatternIndex + while randomPatternIndex == previousPatternIndex && !self.previewPatterns.isEmpty { + randomPatternIndex = Int32.random(in: 0 ..< Int32(self.previewPatterns.count)) + } + if !self.previewPatterns.isEmpty { self.previewPatternIndex = randomPatternIndex } + + self.animatePreviewTransition = true + updateNeeded = true + } + } + if self.lastBackdropChangeTime == nil || now - (self.lastBackdropChangeTime ?? now) >= self.currentInterval * 3.55 { + self.lastBackdropChangeTime = now + + if component.revealedAttributes.contains(.backdrop) { + if self.previewBackdropIndex != -1 { + self.previewBackdropIndex = -1 + self.animateBackdropSwipe = true + updateNeeded = true + } + } else { + let previousBackdropIndex = self.previewBackdropIndex + var randomBackdropIndex = previousBackdropIndex + while randomBackdropIndex == previousBackdropIndex && !self.previewBackdrops.isEmpty { + randomBackdropIndex = Int32.random(in: 0 ..< Int32(self.previewBackdrops.count)) + } + if !self.previewBackdrops.isEmpty { self.previewBackdropIndex = randomBackdropIndex } + + self.animateBackdropSwipe = true + updateNeeded = true + } + } + + if updateNeeded { + self.componentState?.updated(transition: .easeInOut(duration: 0.25)) + self.component?.requestUpdate(.easeInOut(duration: 0.25)) + } + self.applyEdge3DHorizontal() + case .decelerating: + self.applyEdge3DHorizontal() + case .idle, .settled: + self.spinLink?.invalidate() + self.spinLink = nil + } + } + + private let minScaleAtEdgeX: CGFloat = 0.7 + private let yawAtEdgeDegrees: CGFloat = 25.0 + private let edgeFalloffX: CGFloat = 0.25 + + @inline(__always) + private func smoothstep01(_ x: CGFloat) -> CGFloat { + let t = max(0.0, min(1.0, x)) + return t * t * (3.0 - 2.0 * t) + } + + @inline(__always) + private func liveMidX(in container: UIView, of view: UIView) -> CGFloat { + if let pres = view.layer.presentation() { + let p = container.layer.convert(pres.position, from: view.layer.superlayer) + return p.x + } + return view.center.x + } + + @inline(__always) + private func midXInsideAnimatedContainer(in selfView: UIView, container: UIView, hostView: UIView) -> CGFloat { + let contPres = container.layer.presentation() ?? container.layer + let hostPres = hostView.layer.presentation() ?? hostView.layer + + let hostOffsetFromContainerCenter = hostPres.position.x - container.bounds.midX + return contPres.position.x + hostOffsetFromContainerCenter + } + + private func edge3DTransformFor(midX: CGFloat, containerWidth: CGFloat, baseScale: CGFloat = 1.0) -> CATransform3D { + guard containerWidth > 0 else { + return CATransform3DMakeScale(baseScale, baseScale, 1.0) + } + let d = abs((midX - containerWidth * 0.5) / (containerWidth * 0.5)) + + let uRaw = (d - edgeFalloffX) / (1.0 - edgeFalloffX) + let u = smoothstep01(max(0.0, min(1.0, uRaw))) + + let scale = (1.0 - u) * baseScale + (minScaleAtEdgeX * baseScale) * u + let yawSign: CGFloat = (midX < containerWidth * 0.5) ? 1.0 : -1.0 + let yawRadians = (yawAtEdgeDegrees * .pi / 180.0) * u * yawSign + + var t = CATransform3DIdentity + t = CATransform3DRotate(t, yawRadians, 0.0, 1.0, 0.0) + t = CATransform3DScale(t, scale, scale, 1.0) + return t + } + + private func applyEdge3DHorizontal() { + let containerWidth = self.bounds.width + guard containerWidth > 0.0 else { return } + + CATransaction.begin() + CATransaction.setDisableActions(true) + + for w in self.activeWrappers { + guard w.superview === self else { continue } + let midX = liveMidX(in: self, of: w) + if let host = w.subviews.first { + let baseScale = max(0.01, w.bounds.width / max(host.bounds.width, 0.01)) + host.layer.transform = edge3DTransformFor(midX: midX, containerWidth: containerWidth, baseScale: baseScale) + } + } + + for hostView in self.decelItemHosts { + guard let container = self.decelContainer, hostView.superview === container && hostView !== self.decelItemHosts.first else { + continue + } + let midX = midXInsideAnimatedContainer(in: self, container: container, hostView: hostView) + if let host = hostView.subviews.first { + let baseScale = max(0.01, hostView.bounds.width / max(host.bounds.width, 0.01)) + host.layer.transform = edge3DTransformFor(midX: midX, containerWidth: containerWidth, baseScale: baseScale) + } + } + + CATransaction.commit() + } + public func update(component: GiftCompositionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let previousComponent = self.component @@ -140,21 +606,30 @@ public final class GiftCompositionComponent: Component { var files: [Int64: TelegramMediaFile] = [:] var loop = false + + var uniqueSpinContext: (previewAttributes: [StarGift.UniqueGift.Attribute], mainGift: StarGift.UniqueGift)? = nil + switch component.subject { case let .generic(file): animationFile = file self.currentFile = file + self.stopSpinIfNeeded() if let previewTimer = self.previewTimer { previewTimer.invalidate() self.previewTimer = nil } - if !self.fetchedFiles.contains(file.fileId.id) { self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) self.fetchedFiles.insert(file.fileId.id) } - case let .unique(gift): + + case let .unique(previewAttributesOpt, gift): + if let previewTimer = self.previewTimer { + previewTimer.invalidate() + self.previewTimer = nil + } + for attribute in gift.attributes { switch attribute { case let .model(_, file, _): @@ -174,13 +649,23 @@ public final class GiftCompositionComponent: Component { break } } - - if let previewTimer = self.previewTimer { - previewTimer.invalidate() - self.previewTimer = nil + if let previewAttributes = previewAttributesOpt, !previewAttributes.isEmpty { + if self.previewPatternIndex != -1, case let .pattern(_, file, _) = self.previewPatterns[Int(self.previewPatternIndex)] { + patternFile = file + files[file.fileId.id] = file + } + if self.previewBackdropIndex != -1, case let .backdrop(_, _, innerColorValue, outerColorValue, patternColorValue, _, _) = self.previewBackdrops[Int(self.previewBackdropIndex)] { + backgroundColor = UIColor(rgb: UInt32(bitPattern: outerColorValue)) + secondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColorValue)) + patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue)) + } + uniqueSpinContext = (previewAttributes, gift) + } else { + self.stopSpinIfNeeded() } case let .preview(sampleAttributes): loop = true + self.stopSpinIfNeeded() if self.previewModels.isEmpty { var models: [StarGift.UniqueGift.Attribute] = [] @@ -188,14 +673,10 @@ public final class GiftCompositionComponent: Component { var backdrops: [StarGift.UniqueGift.Attribute] = [] for attribute in sampleAttributes { switch attribute { - case .model: - models.append(attribute) - case .pattern: - patterns.append(attribute) - case .backdrop: - backdrops.append(attribute) - default: - break + case .model: models.append(attribute) + case .pattern: patterns.append(attribute) + case .backdrop: backdrops.append(attribute) + default: break } } self.previewModels = models @@ -203,67 +684,72 @@ public final class GiftCompositionComponent: Component { self.previewBackdrops = backdrops } - for case let .model(_, file, _) in self.previewModels { - if !self.fetchedFiles.contains(file.fileId.id) { - self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) - self.fetchedFiles.insert(file.fileId.id) - } + for case let .model(_, file, _) in self.previewModels where !self.fetchedFiles.contains(file.fileId.id) { + self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + self.fetchedFiles.insert(file.fileId.id) } - for case let .pattern(_, file, _) in self.previewModels { - if !self.fetchedFiles.contains(file.fileId.id) { - self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) - self.fetchedFiles.insert(file.fileId.id) - } + for case let .pattern(_, file, _) in self.previewPatterns where !self.fetchedFiles.contains(file.fileId.id) { + self.disposables.add(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + self.fetchedFiles.insert(file.fileId.id) } if !self.previewModels.isEmpty { + if self.previewPatternIndex < 0 { + self.previewPatternIndex = 0 + } + if self.previewBackdropIndex < 0 { + self.previewBackdropIndex = 0 + } if case let .model(_, file, _) = self.previewModels[Int(self.previewModelIndex)] { animationFile = file } - if case let .pattern(_, file, _) = self.previewPatterns[Int(self.previewPatternIndex)] { patternFile = file files[file.fileId.id] = file } - if case let .backdrop(_, _, innerColorValue, outerColorValue, patternColorValue, _, _) = self.previewBackdrops[Int(self.previewBackdropIndex)] { backgroundColor = UIColor(rgb: UInt32(bitPattern: outerColorValue)) secondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColorValue)) patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue)) } } - + if self.previewTimer == nil { self.previewTimer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in - guard let self, !self.previewModels.isEmpty else { - return - } + guard let self, !self.previewModels.isEmpty else { return } self.previewModelIndex = (self.previewModelIndex + 1) % Int32(self.previewModels.count) let previousPatternIndex = self.previewPatternIndex var randomPatternIndex = previousPatternIndex - while randomPatternIndex == previousPatternIndex { + while randomPatternIndex == previousPatternIndex && !self.previewPatterns.isEmpty { randomPatternIndex = Int32.random(in: 0 ..< Int32(self.previewPatterns.count)) } - self.previewPatternIndex = randomPatternIndex + if !self.previewPatterns.isEmpty { self.previewPatternIndex = randomPatternIndex } let previousBackdropIndex = self.previewBackdropIndex var randomBackdropIndex = previousBackdropIndex - while randomBackdropIndex == previousBackdropIndex { + while randomBackdropIndex == previousBackdropIndex && !self.previewBackdrops.isEmpty { randomBackdropIndex = Int32.random(in: 0 ..< Int32(self.previewBackdrops.count)) } - self.previewBackdropIndex = randomBackdropIndex + if !self.previewBackdrops.isEmpty { self.previewBackdropIndex = randomBackdropIndex } self.animatePreviewTransition = true self.componentState?.updated(transition: .easeInOut(duration: 0.25)) + self.component?.requestUpdate(.easeInOut(duration: 0.25)) }, queue: Queue.mainQueue()) self.previewTimer?.start() } } component.externalState?.previewPatternColor = secondBackgroundColor - + + var animateBackdropSwipe = false + if self.animateBackdropSwipe { + animateBackdropSwipe = true + self.animateBackdropSwipe = false + } + var animateTransition = false if self.animatePreviewTransition { animateTransition = true @@ -278,16 +764,24 @@ public final class GiftCompositionComponent: Component { if let backgroundColor { var backgroundTransition = transition - - if animateTransition, let backgroundView = self.background.view as? PeerInfoCoverComponent.View { - backgroundView.animateTransition() + if let backgroundView = self.background.view as? PeerInfoCoverComponent.View { + if animateTransition { + var bounce = true + var background = true + if case .unique = component.subject { + bounce = self.previewPatternIndex == -1 + background = false + } + backgroundView.animateTransition(background: background, bounce: bounce) + } + if animateBackdropSwipe { + backgroundView.animateSwipeTransition() + } } - var avatarCenter = CGPoint(x: availableSize.width / 2.0, y: 104.0) if let _ = component.animationScale { avatarCenter = CGPoint(x: avatarCenter.x, y: 67.0) } - let _ = self.background.update( transition: backgroundTransition, component: AnyComponent(PeerInfoCoverComponent( @@ -311,7 +805,6 @@ public final class GiftCompositionComponent: Component { backgroundView.clipsToBounds = true backgroundView.isUserInteractionEnabled = false self.insertSubview(backgroundView, at: 0) - if previousComponent != nil { backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } @@ -323,9 +816,99 @@ public final class GiftCompositionComponent: Component { backgroundView.removeFromSuperview() }) } - + let iconSize = CGSize(width: 136.0, height: 136.0) + if let (previewAttributes, mainGift) = uniqueSpinContext { + var mainModelFile: TelegramMediaFile? + for attribute in mainGift.attributes { + if case let .model(_, file, _) = attribute { mainModelFile = file; break } + } + + var models: [StarGift.UniqueGift.Attribute] = [] + for attribute in previewAttributes { + if case let .model(_, file, _) = attribute, + file.fileId.id != mainModelFile?.fileId.id { + models.append(attribute) + } + } + + if models.isEmpty, let _ = mainModelFile { + return availableSize + } + + for case let .model(_, file, _) in models where !self.fetchedFiles.contains(file.fileId.id) { + self.disposables.add(freeMediaFileResourceInteractiveFetched( + account: component.context.account, + userLocation: .other, + fileReference: .standalone(media: file), + resource: file.resource + ).start()) + self.fetchedFiles.insert(file.fileId.id) + } + if let mainModelFile, !self.fetchedFiles.contains(mainModelFile.fileId.id) { + self.disposables.add(freeMediaFileResourceInteractiveFetched( + account: component.context.account, + userLocation: .other, + fileReference: .standalone(media: mainModelFile), + resource: mainModelFile.resource + ).start()) + self.fetchedFiles.insert(mainModelFile.fileId.id) + } + + let wasAnimatingModel = previousComponent != nil && !(previousComponent!.revealedAttributes.contains(.model)) + let isAnimatingModel = !component.revealedAttributes.contains(.model) + + let wasAnimating = wasAnimatingModel + let nowAnimating = isAnimatingModel + + if nowAnimating { + if let disappearing = self.animationNode { + self.animationNode = nil + disappearing.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { _ in + disappearing.view.removeFromSuperview() + }) + } + } + + let scaleValue: CGFloat = component.animationScale ?? 1.0 + + if nowAnimating && (!wasAnimating || self.spinState != .spinning) { + self.startSpinningUnique( + availableSize: availableSize, + iconSize: iconSize, + scale: scaleValue, + pool: models + ) + } else if !nowAnimating && wasAnimating { + var tail = Array(models.shuffled().prefix(6)) + if let mainModelFile { + tail.append(.model(name: "", file: mainModelFile, rarity: 0)) + } + self.beginDecelerationWithQueue( + tail: tail, + availableSize: availableSize, + iconSize: iconSize, + scale: scaleValue + ) + } else if self.spinState == .spinning { + let centerY = 88.0 + (component.animationOffset?.y ?? 0.0) + self.spinGeom = SpinGeom( + availableSize: availableSize, + iconSize: iconSize, + scale: scaleValue, + centerX: availableSize.width / 2.0 + (component.animationOffset?.x ?? 0.0), + centerY: centerY + ) + } + + return availableSize + } + + if self.spinState != .idle && self.spinState != .settled { + self.stopSpinIfNeeded() + } + var startFromIndex: Int? var animationTransition = transition if animateTransition, let disappearingAnimationNode = self.animationNode { @@ -337,46 +920,39 @@ public final class GiftCompositionComponent: Component { animationTransition = .immediate } - if let file = animationFile { - let animationNode: AnimatedStickerNode - if self.animationNode == nil { - animationTransition = .immediate - animationNode = DefaultAnimatedStickerNodeImpl() - animationNode.isUserInteractionEnabled = false - self.animationNode = animationNode - - self.addSubview(animationNode.view) - - let pathPrefix = component.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) - animationNode.setup(source: AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(iconSize.width * 1.6), height: Int(iconSize.height * 1.6), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix)) - - if let startFromIndex { - if let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl { - animationNode.playbackMode = loop ? .loop : .once - } - animationNode.play(firstFrame: false, fromIndex: startFromIndex) - } else { - if loop { - animationNode.playLoop() - } else { - animationNode.playOnce() - } - } - animationNode.visibility = true - animationNode.updateLayout(size: iconSize) - - if animateTransition { - animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } + if let file = animationFile, self.animationNode == nil { + animationTransition = .immediate + let node = DefaultAnimatedStickerNodeImpl() + node.isUserInteractionEnabled = false + self.animationNode = node + self.addSubview(node.view) + let pathPrefix = component.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) + node.setup(source: AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker), + width: Int(iconSize.width * 1.6), height: Int(iconSize.height * 1.6), + playbackMode: loop ? .loop : .once, + mode: .direct(cachePathPrefix: pathPrefix)) + if let startFromIndex { + node.play(firstFrame: false, fromIndex: startFromIndex) + } else { + if loop { node.playLoop() } else { node.playOnce() } + } + node.visibility = true + node.updateLayout(size: iconSize) + if animateTransition { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } } + if let animationNode = self.animationNode { let offset = component.animationOffset ?? .zero var size = CGSize(width: iconSize.width, height: iconSize.height) if let scale = component.animationScale { size = CGSize(width: size.width * scale, height: size.height * scale) } - let animationFrame = CGRect(origin: CGPoint(x: availableSize.width / 2.0 + offset.x - size.width / 2.0, y: 88.0 + offset.y - size.height / 2.0), size: size) + let animationFrame = CGRect( + origin: CGPoint(x: availableSize.width / 2.0 + offset.x - size.width / 2.0, y: 88.0 + offset.y - size.height / 2.0), + size: size + ) animationNode.layer.bounds = CGRect(origin: .zero, size: iconSize) animationTransition.setPosition(layer: animationNode.layer, position: animationFrame.center) animationTransition.setScale(layer: animationNode.layer, scale: size.width / iconSize.width) @@ -405,7 +981,7 @@ public final class GiftCompositionComponent: Component { }) } } - + return availableSize } } @@ -424,7 +1000,6 @@ private final class StarsEffectLayer: SimpleLayer { override init() { super.init() - self.addSublayer(self.emitterLayer) } diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index 6af3bfcb3f..901d9dfd33 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -134,6 +134,7 @@ public final class GiftItemComponent: Component { case preview case grid case select + case buttonIcon } let context: AccountContext @@ -153,6 +154,7 @@ public final class GiftItemComponent: Component { let isSelected: Bool let isPinned: Bool let isEditing: Bool + let isDateLocked: Bool let mode: Mode let action: (() -> Void)? let contextAction: ((UIView, ContextGesture) -> Void)? @@ -175,6 +177,7 @@ public final class GiftItemComponent: Component { isSelected: Bool = false, isPinned: Bool = false, isEditing: Bool = false, + isDateLocked: Bool = false, mode: Mode = .generic, action: (() -> Void)? = nil, contextAction: ((UIView, ContextGesture) -> Void)? = nil @@ -196,6 +199,7 @@ public final class GiftItemComponent: Component { self.isSelected = isSelected self.isPinned = isPinned self.isEditing = isEditing + self.isDateLocked = isDateLocked self.mode = mode self.action = action self.contextAction = contextAction @@ -253,6 +257,9 @@ public final class GiftItemComponent: Component { if lhs.isEditing != rhs.isEditing { return false } + if lhs.isDateLocked != rhs.isDateLocked { + return false + } if lhs.mode != rhs.mode { return false } @@ -297,6 +304,7 @@ public final class GiftItemComponent: Component { private var iconBackground: UIVisualEffectView? private var hiddenIcon: UIImageView? private var pinnedIcon: UIImageView? + private var dateLockedIcon: UIImageView? private var resellBackground: BlurredBackgroundView? private let reselLabel = ComponentView() @@ -375,6 +383,10 @@ public final class GiftItemComponent: Component { size = availableSize iconSize = CGSize(width: floor(size.width * 0.6), height: floor(size.width * 0.6)) cornerRadius = 4.0 + case .buttonIcon: + size = CGSize(width: 26.0, height: 26.0) + iconSize = size + cornerRadius = 0.0 } var backgroundSize = size if case .grid = component.mode { @@ -464,7 +476,7 @@ public final class GiftItemComponent: Component { break } } - + if let animationFile { emoji = ChatTextInputTextCustomEmojiAttribute( interactivelySelectedFromPackId: nil, @@ -476,6 +488,13 @@ public final class GiftItemComponent: Component { } } + if case .buttonIcon = component.mode { + backgroundColor = nil + secondBackgroundColor = nil + patternColor = nil + placeholderColor = component.theme.list.mediaPlaceholderColor + } + var animationTransition = transition if self.animationLayer == nil || self.animationFile?.fileId != animationFile?.fileId, let emoji { animationTransition = .immediate @@ -890,6 +909,22 @@ public final class GiftItemComponent: Component { }) pinnedIcon.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) } + + if component.isDateLocked { + let dateLockedIcon: UIImageView + if let currentIcon = self.dateLockedIcon { + dateLockedIcon = currentIcon + } else { + dateLockedIcon = UIImageView(image: UIImage(bundleImageName: "Peer Info/DateLockedIcon")?.withRenderingMode(.alwaysTemplate)) + self.dateLockedIcon = dateLockedIcon + self.addSubview(dateLockedIcon) + } + dateLockedIcon.frame = CGRect(origin: CGPoint(x: 3.0, y: 3.0), size: CGSize(width: 24.0, height: 24.0)) + dateLockedIcon.tintColor = component.theme.list.itemDestructiveColor + } else if let dateLockedIcon = self.dateLockedIcon { + self.dateLockedIcon = nil + dateLockedIcon.removeFromSuperview() + } if component.isHidden && !component.isEditing { let hiddenIcon: UIImageView diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index c9fdf2a450..d38abd89a4 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -143,6 +143,8 @@ final class GiftOptionsScreenComponent: Component { private var starsFilter: StarsFilter = .all private var switchingFilter = false + private var loadingGiftId: Int64? + private var _effectiveStarGifts: ([StarGift], StarsFilter)? private var effectiveStarGifts: [StarGift]? { get { @@ -274,6 +276,122 @@ final class GiftOptionsScreenComponent: Component { func scrollViewDidScroll(_ scrollView: UIScrollView) { self.updateScrolling(interactive: true, transition: self.nextScrollTransition ?? .immediate) } + + private func openGift(gift: StarGift, skipDateCheck: Bool = false) { + guard let component = self.component, let environment = self.environment else { + return + } + let context = component.context + let strings = environment.strings + let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + let starGift = gift + + if let controller = environment.controller() as? GiftOptionsScreen { + let mainController: ViewController + if let parentController = controller.parentController() { + mainController = parentController + } else { + mainController = controller + } + if case let .generic(gift) = gift { + if let lockedUntilDate = gift.lockedUntilDate, currentTime < lockedUntilDate { + self.loadingGiftId = gift.id + Queue.mainQueue().after(0.25) { + if self.loadingGiftId != nil { + self.state?.updated() + } + } + + let _ = (component.context.engine.payments.checkCanSendStarGift(giftId: gift.id) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] result in + guard let self, let controller else { + return + } + self.loadingGiftId = nil + self.state?.updated() + + switch result { + case .available: + self.openGift(gift: starGift, skipDateCheck: true) + case let .unavailable(text, entities): + let theme = AlertControllerTheme(presentationData: component.context.sharedContext.currentPresentationData.with { $0 }) + let font = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) + let boldFont = Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0)) + let attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primaryColor, linkColor: theme.accentColor, baseFont: font, linkFont: font, boldFont: boldFont, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: nil, paragraphAlignment: .center) + + var dismissImpl: (() -> Void)? + let alertController = textAlertController(theme: theme, title: NSAttributedString(string: strings.Gift_Options_GiftLocked_Title, font: Font.semibold(theme.baseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: { + dismissImpl?() + })], actionLayout: .horizontal, dismissOnOutsideTap: true, linkAction: { [weak controller] attributes, _ in + if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + dismissImpl?() + if let navigationController = controller?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {}) + } + } + }) + dismissImpl = { [weak alertController] in + alertController?.dismissAnimated() + } + controller.present(alertController, in: .window(.root)) + case .failed: + break + } + }) + return + } + + if let perUserLimit = gift.perUserLimit, perUserLimit.remains == 0 { + let text = environment.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit.total) + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: gift.file, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false }) + mainController.present(controller, in: .current) + return + } + if gift.flags.contains(.requiresPremium) && !component.context.isPremium { + let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .premiumGift(gift.file), forceDark: false, dismissed: nil) + mainController.push(controller) + return + } + + if let availability = gift.availability, availability.remains == 0 { + if availability.resale > 0 { + let storeController = component.context.sharedContext.makeGiftStoreController( + context: component.context, + peerId: component.peerId, + gift: gift + ) + mainController.push(storeController) + } else { + let giftController = GiftViewScreen( + context: component.context, + subject: .soldOutGift(gift) + ) + mainController.push(giftController) + } + } else { + var forceUnique: Bool? + if let disallowedGifts = self.state?.disallowedGifts { + if disallowedGifts.contains(.limited) && !disallowedGifts.contains(.unique) { + forceUnique = true + } else if !disallowedGifts.contains(.limited) && disallowedGifts.contains(.unique) { + forceUnique = false + } + } + + let giftController = GiftSetupScreen( + context: component.context, + peerId: component.peerId, + subject: .starGift(gift, forceUnique), + completion: component.completion + ) + mainController.push(giftController) + } + } else if case let .unique(gift) = gift { + self.transferGift(gift) + } + } + } private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) { guard let environment = self.environment, let component = self.component else { @@ -344,8 +462,6 @@ final class GiftOptionsScreenComponent: Component { var validIds: [AnyHashable] = [] var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: self.starsItemsOrigin), size: starsOptionSize) - let controller = environment.controller - for gift in starGifts { var isVisible = false if visibleBounds.intersects(itemFrame) { @@ -371,6 +487,7 @@ final class GiftOptionsScreenComponent: Component { var ribbon: GiftItemComponent.Ribbon? var outline: GiftItemComponent.Outline? var isSoldOut = false + var isLoading = false switch gift { case let .generic(gift): if let _ = gift.soldOut { @@ -392,14 +509,17 @@ final class GiftOptionsScreenComponent: Component { color: .blue ) } - - if gift.flags.contains(.requiresPremium) { + if !isSoldOut && gift.flags.contains(.requiresPremium) { ribbon = GiftItemComponent.Ribbon( text: environment.strings.Gift_Options_Gift_Premium, color: .orange ) outline = .orange } + + if gift.id == self.loadingGiftId { + isLoading = true + } case let .unique(gift): var ribbonColor: GiftItemComponent.Ribbon.Color = .blue for attribute in gift.attributes { @@ -415,6 +535,7 @@ final class GiftOptionsScreenComponent: Component { ) } + var lockedUntilDate: Int32? let subject: GiftItemComponent.Subject switch gift { case let .generic(gift): @@ -428,6 +549,7 @@ final class GiftOptionsScreenComponent: Component { } else { subject = .starGift(gift: gift, price: "# \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))") } + lockedUntilDate = gift.lockedUntilDate case let .unique(gift): subject = .uniqueGift(gift: gift, price: nil) } @@ -445,71 +567,17 @@ final class GiftOptionsScreenComponent: Component { subject: subject, ribbon: ribbon, outline: outline, - isSoldOut: isSoldOut + isLoading: isLoading, + isSoldOut: isSoldOut, + isDateLocked: lockedUntilDate != nil ) ), effectAlignment: .center, action: { [weak self] in - if let self, let component = self.component { - if let controller = controller() as? GiftOptionsScreen { - let mainController: ViewController - if let parentController = controller.parentController() { - mainController = parentController - } else { - mainController = controller - } - if case let .generic(gift) = gift { - if let perUserLimit = gift.perUserLimit, perUserLimit.remains == 0 { - let text = environment.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit.total) - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: gift.file, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false }) - mainController.present(controller, in: .current) - return - } - if gift.flags.contains(.requiresPremium) && !component.context.isPremium { - let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .premiumGift(gift.file), forceDark: false, dismissed: nil) - mainController.push(controller) - return - } - - if let availability = gift.availability, availability.remains == 0 { - if availability.resale > 0 { - let storeController = component.context.sharedContext.makeGiftStoreController( - context: component.context, - peerId: component.peerId, - gift: gift - ) - mainController.push(storeController) - } else { - let giftController = GiftViewScreen( - context: component.context, - subject: .soldOutGift(gift) - ) - mainController.push(giftController) - } - } else { - var forceUnique: Bool? - if let disallowedGifts = self.state?.disallowedGifts { - if disallowedGifts.contains(.limited) && !disallowedGifts.contains(.unique) { - forceUnique = true - } else if !disallowedGifts.contains(.limited) && disallowedGifts.contains(.unique) { - forceUnique = false - } - } - - let giftController = GiftSetupScreen( - context: component.context, - peerId: component.peerId, - subject: .starGift(gift, forceUnique), - completion: component.completion - ) - mainController.push(giftController) - } - } else if case let .unique(gift) = gift { - self.transferGift(gift) - } - } + guard let self else { + return } + self.openGift(gift: gift) }, animateAlpha: false ) diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift index c91d2fb778..1606a4ee30 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift @@ -239,7 +239,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode { case let .starGift(gift): media = [ TelegramMediaAction( - action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: gift.upgradeStars != nil, upgradeStars: item.upgradeStars, isRefunded: false, upgradeMessageId: nil, peerId: nil, senderId: nil, savedId: nil) + action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: gift.upgradeStars != nil, upgradeStars: item.upgradeStars, isRefunded: false, isPrepaidUpgrade: false, upgradeMessageId: nil, peerId: nil, senderId: nil, savedId: nil, prepaidUpgradeHash: nil, giftMessageId: nil, upgradeSeparate: false) ) ] } diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index e976012283..b7c8c81f79 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -752,6 +752,10 @@ final class GiftSetupScreenComponent: Component { } else if isChannelGift { navigationTitleString = environment.strings.Gift_SendChannel_Title } else { + var peerName = peerName + if peerName.count > 22 { + peerName = "\(peerName.prefix(22))…" + } navigationTitleString = environment.strings.Gift_Send_TitleTo(peerName).string } let navigationTitleSize = self.navigationTitle.update( diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD index a2e66c96b7..47ded4318a 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD @@ -53,6 +53,8 @@ swift_library( "//submodules/ActivityIndicator", "//submodules/TelegramUI/Components/TabSelectorComponent", "//submodules/TelegramUI/Components/Stars/BalanceNeededScreen", + "//submodules/TelegramUI/Components/ChatThemeScreen", + "//submodules/ImageBlur", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPagerComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPagerComponent.swift index 83def8af34..e3fd5656f3 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPagerComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPagerComponent.swift @@ -4,6 +4,7 @@ import ComponentFlow import Display import TelegramPresentationData import ViewControllerComponent +import SheetComponent import AccountContext final class GiftPagerComponent: Component { @@ -34,6 +35,7 @@ final class GiftPagerComponent: Component { let items: [Item] let index: Int let itemSpacing: CGFloat + let isSwitching: Bool let updated: (CGFloat, Int) -> Void public init( @@ -41,12 +43,14 @@ final class GiftPagerComponent: Component { items: [Item], index: Int = 0, itemSpacing: CGFloat = 0.0, + isSwitching: Bool = false, updated: @escaping (CGFloat, Int) -> Void ) { self.context = context self.items = items self.index = index self.itemSpacing = itemSpacing + self.isSwitching = isSwitching self.updated = updated } @@ -60,10 +64,14 @@ final class GiftPagerComponent: Component { if lhs.itemSpacing != rhs.itemSpacing { return false } + if lhs.isSwitching != rhs.isSwitching { + return false + } return true } final class View: UIView, UIScrollViewDelegate { + private let dimView: UIView private let scrollView: UIScrollView private var itemViews: [AnyHashable: ComponentHostView] = [:] @@ -71,7 +79,11 @@ final class GiftPagerComponent: Component { private var environment: Environment? override init(frame: CGRect) { + self.dimView = UIView() + self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.4) + self.scrollView = UIScrollView(frame: frame) + self.scrollView.clipsToBounds = true self.scrollView.isPagingEnabled = true self.scrollView.showsHorizontalScrollIndicator = false self.scrollView.showsVerticalScrollIndicator = false @@ -84,8 +96,9 @@ final class GiftPagerComponent: Component { super.init(frame: frame) - self.scrollView.delegate = self + self.addSubview(self.dimView) + self.scrollView.delegate = self self.addSubview(self.scrollView) } @@ -93,6 +106,15 @@ final class GiftPagerComponent: Component { fatalError("init(coder:) has not been implemented") } + private func animateIn() { + self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + + } + + private func animateOut() { + self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + } + private var isSwiping: Bool = false private var lastScrollTime: TimeInterval = 0 private let swipeInactiveThreshold: TimeInterval = 0.5 @@ -128,33 +150,43 @@ final class GiftPagerComponent: Component { self.ignoreContentOffsetChange = false } + private var previousIsDisplaying: Bool = false + private var isUpdating = true func update(component: GiftPagerComponent, availableSize: CGSize, environment: Environment, transition: ComponentTransition) -> CGSize { self.isUpdating = true defer { self.isUpdating = false } + + transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil) + var validIds: [AnyHashable] = [] + let previousComponent = self.component self.component = component self.environment = environment - let firstTime = self.itemViews.isEmpty + var countDecreased = false + if let previousComponent, previousComponent.items.count != component.items.count { + countDecreased = true + } + let firstTime = self.itemViews.isEmpty || countDecreased let itemWidth = availableSize.width - let totalWidth = itemWidth * CGFloat(component.items.count) + component.itemSpacing * CGFloat(max(0, component.items.count - 1)) + let totalWidth = itemWidth * CGFloat(component.items.count) + component.itemSpacing * 2.0 * CGFloat(max(0, component.items.count)) let contentSize = CGSize(width: totalWidth, height: availableSize.height) if self.scrollView.contentSize != contentSize { self.scrollView.contentSize = contentSize } - let scrollFrame = CGRect(origin: .zero, size: availableSize) + let scrollFrame = CGRect(origin: CGPoint(x: -component.itemSpacing / 2.0, y: 0.0), size: CGSize(width: availableSize.width + component.itemSpacing * 2.0, height: availableSize.height)) if self.scrollView.frame != scrollFrame { self.scrollView.frame = scrollFrame } if firstTime { - let initialOffset = CGFloat(component.index) * (itemWidth + component.itemSpacing) + let initialOffset = CGFloat(component.index) * (itemWidth + component.itemSpacing * 2.0) self.scrollView.contentOffset = CGPoint(x: initialOffset, y: 0.0) var position: CGFloat @@ -172,7 +204,7 @@ final class GiftPagerComponent: Component { var i = 0 for item in component.items { - let itemOriginX = (itemWidth + component.itemSpacing) * CGFloat(i) + let itemOriginX = component.itemSpacing * 0.5 + (itemWidth + component.itemSpacing * 2.0) * CGFloat(i) let itemFrame = CGRect(origin: CGPoint(x: itemOriginX, y: 0.0), size: CGSize(width: itemWidth, height: availableSize.height)) let centerDelta = itemFrame.midX - viewportCenter @@ -216,17 +248,39 @@ final class GiftPagerComponent: Component { itemView.frame = itemFrame } + var animateTransitionMovement = false var removeIds: [AnyHashable] = [] for (id, itemView) in self.itemViews { if !validIds.contains(id) { removeIds.append(id) - itemView.removeFromSuperview() + if countDecreased && !transition.animation.isImmediate { + self.addSubview(itemView) + itemView.center = CGPoint(x: self.scrollView.frame.width / 2.0 - component.itemSpacing, y: self.scrollView.frame.height / 2.0) + transition.setPosition(view: itemView, position: CGPoint(x: itemView.center.x - itemView.frame.width, y: itemView.center.y), completion: { _ in + itemView.removeFromSuperview() + }) + animateTransitionMovement = true + } else { + itemView.removeFromSuperview() + } } } for id in removeIds { self.itemViews.removeValue(forKey: id) } - + + if animateTransitionMovement { + transition.animatePosition(view: self.scrollView, from: CGPoint(x: self.scrollView.frame.width, y: 0.0), to: .zero, additive: true) + } + + let viewEnvironment = environment[ViewControllerComponentContainer.Environment.self].value + if let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) { + self.animateIn() + } else if self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) { + self.animateOut() + } + self.previousIsDisplaying = viewEnvironment.isVisible + return availableSize } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift new file mode 100644 index 0000000000..88a12392aa --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift @@ -0,0 +1,866 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit +import AccountContext +import TelegramPresentationData +import PresentationDataUtils +import ComponentFlow +import ViewControllerComponent +import SheetComponent +import MultilineTextComponent +import MultilineTextWithEntitiesComponent +import BundleIconComponent +import ButtonComponent +import Markdown +import BalancedTextComponent +import AvatarNode +import TextFormat +import TelegramStringFormatting +import StarsAvatarComponent +import EmojiTextAttachmentView +import EmojiStatusComponent +import UndoUI +import PlainButtonComponent +import TooltipUI +import GiftAnimationComponent +import LottieComponent +import ContextUI +import TelegramNotices +import GiftItemComponent + +private final class GiftValueSheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gift: StarGift + let valueInfo: StarGift.UniqueGift.ValueInfo + let animateOut: ActionSlot> + let getController: () -> ViewController? + + init( + context: AccountContext, + gift: StarGift, + valueInfo: StarGift.UniqueGift.ValueInfo, + animateOut: ActionSlot>, + getController: @escaping () -> ViewController? + ) { + self.context = context + self.gift = gift + self.valueInfo = valueInfo + self.animateOut = animateOut + self.getController = getController + } + + static func ==(lhs: GiftValueSheetContent, rhs: GiftValueSheetContent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.gift != rhs.gift { + return false + } + if lhs.valueInfo != rhs.valueInfo { + return false + } + return true + } + + final class State: ComponentState { + let lastSalePriceTag = GenericComponentViewTag() + let floorPriceTag = GenericComponentViewTag() + let averagePriceTag = GenericComponentViewTag() + + private let context: AccountContext + private let animateOut: ActionSlot> + private let getController: () -> ViewController? + + private var disposable: Disposable? + var initialized = false + + var starGiftsMap: [Int64: StarGift.Gift] = [:] + + var cachedStarImage: (UIImage, PresentationTheme)? + var cachedSmallStarImage: (UIImage, PresentationTheme)? + var cachedSubtitleStarImage: (UIImage, PresentationTheme)? + var cachedTonImage: (UIImage, PresentationTheme)? + + var cachedChevronImage: (UIImage, PresentationTheme)? + var cachedSmallChevronImage: (UIImage, PresentationTheme)? + + init( + context: AccountContext, + animateOut: ActionSlot>, + getController: @escaping () -> ViewController? + ) { + self.context = context + self.animateOut = animateOut + self.getController = getController + + super.init() + + self.disposable = (context.engine.payments.cachedStarGifts() + |> deliverOnMainQueue).startStrict(next: { [weak self] starGifts in + if let strongSelf = self { + var starGiftsMap: [Int64: StarGift.Gift] = [:] + if let starGifts { + for gift in starGifts { + if case let .generic(gift) = gift { + starGiftsMap[gift.id] = gift + } + } + } + strongSelf.starGiftsMap = starGiftsMap + strongSelf.updated(transition: .immediate) + } + }) + } + + deinit { + self.disposable?.dispose() + } + + func showAttributeInfo(tag: Any, text: String) { + guard let controller = self.getController() as? GiftValueScreen else { + return + } + controller.dismissAllTooltips() + + guard let sourceView = controller.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else { + return + } + + let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) + let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .markdown(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in + return .dismiss(consume: false) + }) + controller.present(tooltipController, in: .current) + } + + func openGiftResale(gift: StarGift.Gift) { + guard let controller = self.getController() as? GiftValueScreen else { + return + } + let storeController = self.context.sharedContext.makeGiftStoreController( + context: self.context, + peerId: self.context.account.peerId, + gift: gift + ) + controller.push(storeController) + } + + func openGiftFragmentResale(url: String) { + guard let controller = self.getController() as? GiftValueScreen, let navigationController = controller.navigationController as? NavigationController else { + return + } + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + + func dismiss(animated: Bool) { + guard let controller = self.getController() as? GiftValueScreen else { + return + } + if animated { + controller.dismissAllTooltips() + self.animateOut.invoke(Action { [weak controller] _ in + controller?.dismiss(completion: nil) + }) + } else { + controller.dismiss(animated: false) + } + } + } + + func makeState() -> State { + return State(context: self.context, animateOut: self.animateOut, getController: self.getController) + } + + static var body: Body { + let buttons = Child(ButtonsComponent.self) + let animation = Child(GiftCompositionComponent.self) + + let titleBackground = Child(RoundedRectangle.self) + let title = Child(MultilineTextComponent.self) + + let description = Child(MultilineTextComponent.self) + + let table = Child(TableComponent.self) + + let telegramSaleButton = Child(PlainButtonComponent.self) + let fragmentSaleButton = Child(PlainButtonComponent.self) + + let giftCompositionExternalState = GiftCompositionComponent.ExternalState() + + return { context in + let environment = context.environment[ViewControllerComponentContainer.Environment.self].value + + let component = context.component + let theme = environment.theme + let strings = environment.strings + let dateTimeFormat = environment.dateTimeFormat + + let state = context.state + + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + + let titleString: String = formatCurrencyAmount(component.valueInfo.value, currency: component.valueInfo.currency) + var giftTitle: String = "" + var giftCollectionTitle: String = "" + var animationFile: TelegramMediaFile? + var giftIconSubject: GiftItemComponent.Subject? + var genericGift: StarGift.Gift? + + switch component.gift { + case let .generic(gift): + animationFile = gift.file + giftIconSubject = .starGift(gift: gift, price: "") + case let .unique(gift): + for attribute in gift.attributes { + if case let .model(_, file, _) = attribute { + animationFile = file + } + } + giftCollectionTitle = gift.title + giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: dateTimeFormat))" + + if let gift = state.starGiftsMap[gift.giftId] { + giftIconSubject = .starGift(gift: gift, price: "") + genericGift = gift + } + } + + let buttons = buttons.update( + component: ButtonsComponent( + theme: theme, + isOverlay: false, + showMoreButton: false, + closePressed: { [weak state] in + guard let state else { + return + } + state.dismiss(animated: true) + }, + morePressed: { _, _ in + } + ), + availableSize: CGSize(width: 30.0, height: 30.0), + transition: context.transition + ) + + var originY: CGFloat = 0.0 + + let headerHeight: CGFloat = 210.0 + let headerSubject: GiftCompositionComponent.Subject? + if let animationFile { + headerSubject = .generic(animationFile) + } else { + headerSubject = nil + } + + if let headerSubject { + let animation = animation.update( + component: GiftCompositionComponent( + context: component.context, + theme: environment.theme, + subject: headerSubject, + animationOffset: nil, + animationScale: nil, + displayAnimationStars: false, + externalState: giftCompositionExternalState, + requestUpdate: { [weak state] _ in + state?.updated() + } + ), + availableSize: CGSize(width: context.availableSize.width, height: headerHeight), + transition: context.transition + ) + context.add(animation + .position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight / 2.0)) + ) + } + originY += headerHeight + + let title = title.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: titleString, + font: Font.with(size: 24.0, design: .round, weight: .bold), + textColor: theme.list.itemCheckColors.foregroundColor, + paragraphAlignment: .center + )), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + let titleBackground = titleBackground.update( + component: RoundedRectangle(color: theme.actionSheet.controlAccentColor, cornerRadius: 24.0), + environment: {}, + availableSize: CGSize(width: title.size.width + 32.0, height: 48.0), + transition: .immediate + ) + context.add(titleBackground + .position(CGPoint(x: context.availableSize.width / 2.0, y: 187.0)) + ) + + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: 187.0)) + ) + + var descriptionText: String + if component.valueInfo.valueIsAverage { + descriptionText = strings.Gift_Value_DescriptionAveragePrice(giftCollectionTitle).string + } else { + if component.valueInfo.isLastSaleOnFragment { + descriptionText = strings.Gift_Value_DescriptionLastPriceFragment(giftTitle).string + } else { + descriptionText = strings.Gift_Value_DescriptionLastPriceTelegram(giftTitle).string + } + } + if !descriptionText.isEmpty { + let linkColor = theme.actionSheet.controlAccentColor + if state.cachedSmallStarImage == nil || state.cachedSmallStarImage?.1 !== environment.theme { + state.cachedSmallStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/ButtonStar"), color: .white)!, theme) + } + if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme { + state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme) + } + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = theme.list.itemPrimaryTextColor + + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + descriptionText = descriptionText.replacingOccurrences(of: " >]", with: "\u{00A0}>]") + let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString + if let range = attributedString.string.range(of: "*"), let starImage = state.cachedSmallStarImage?.0 { + attributedString.addAttribute(.font, value: Font.regular(13.0), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string)) + } + if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { + attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string)) + } + + let description = description.update( + component: MultilineTextComponent( + text: .plain(attributedString), + horizontalAlignment: .center, + maximumNumberOfLines: 5, + lineSpacing: 0.2, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { _, _ in + } + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + context.add(description + .position(CGPoint(x: context.availableSize.width / 2.0, y: 231.0 + description.size.height / 2.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + originY += description.size.height + originY += 42.0 + } else { + originY += 9.0 + } + + let tableFont = Font.regular(15.0) + let tableTextColor = theme.list.itemPrimaryTextColor + + var tableItems: [TableComponent.Item] = [] + + tableItems.append(.init( + id: "initialDate", + title: strings.Gift_Value_InitialSale, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: component.valueInfo.initialSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor))) + ) + )) + + let valueString = "⭐️\(formatStarsAmountText(StarsAmount(value: component.valueInfo.initialSaleStars, nanos: 0), dateTimeFormat: dateTimeFormat)) (≈\(formatCurrencyAmount(component.valueInfo.initialSalePrice, currency: component.valueInfo.currency)))" + let valueAttributedString = NSMutableAttributedString(string: valueString, font: tableFont, textColor: tableTextColor) + let range = (valueAttributedString.string as NSString).range(of: "⭐️") + if range.location != NSNotFound { + valueAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: range) + valueAttributedString.addAttribute(.baselineOffset, value: 1.0, range: range) + } + + tableItems.append(.init( + id: "initialPrice", + title: strings.Gift_Value_InitialPrice, + component: AnyComponent(MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: theme.list.mediaPlaceholderColor, + text: .plain(valueAttributedString), + maximumNumberOfLines: 0 + )), + insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0) + )) + + if let lastSaleDate = component.valueInfo.lastSaleDate { + tableItems.append(.init( + id: "lastDate", + title: strings.Gift_Value_LastSale, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: lastSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor))) + ) + )) + } + + if let lastSalePrice = component.valueInfo.lastSalePrice { + let lastSalePriceString = formatCurrencyAmount(lastSalePrice, currency: component.valueInfo.currency) + let tag = state.lastSalePriceTag + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: lastSalePriceString, font: tableFont, textColor: tableTextColor))) + ) + ) + ) + + let percentage = Int32(floor(Double(lastSalePrice) / Double(component.valueInfo.initialSalePrice) * 100.0 - 100.0)) + let percentageString = (percentage > 0 ? "+\(percentage)" : "\(percentage)") + "%" + + items.append(AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: component.context, + text: percentageString, + color: theme.list.itemAccentColor + )), + action: { [weak state] in + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_LastPriceInfo(lastSalePriceString, giftCollectionTitle).string) + + } + ).tagged(tag)) + )) + let itemComponent = AnyComponent( + HStack(items, spacing: 4.0) + ) + tableItems.append(.init( + id: "lastPrice", + title: strings.Gift_Value_LastPrice, + hasBackground: false, + component: itemComponent + )) + } + + if let floorPrice = component.valueInfo.floorPrice { + let floorPriceString = formatCurrencyAmount(floorPrice, currency: component.valueInfo.currency) + let tag = state.floorPriceTag + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: floorPriceString, font: tableFont, textColor: tableTextColor))) + ) + ) + ) + items.append(AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: component.context, + text: "?", + color: theme.list.itemAccentColor + )), + action: { [weak state] in + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_MinimumPriceInfo(floorPriceString, giftCollectionTitle).string) + } + ).tagged(tag)) + )) + let itemComponent = AnyComponent( + HStack(items, spacing: 4.0) + ) + tableItems.append(.init( + id: "floorPrice", + title: strings.Gift_Value_MinimumPrice, + hasBackground: false, + component: itemComponent + )) + } + + if let averagePrice = component.valueInfo.averagePrice { + let averagePriceString = formatCurrencyAmount(averagePrice, currency: component.valueInfo.currency) + let tag = state.averagePriceTag + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: averagePriceString, font: tableFont, textColor: tableTextColor))) + ) + ) + ) + items.append(AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: component.context, + text: "?", + color: theme.list.itemAccentColor + )), + action: { [weak state] in + state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_AveragePriceInfo(averagePriceString, giftCollectionTitle).string) + } + ).tagged(tag)) + )) + let itemComponent = AnyComponent( + HStack(items, spacing: 4.0) + ) + tableItems.append(.init( + id: "averagePrice", + title: strings.Gift_Value_AveragePrice, + hasBackground: false, + component: itemComponent + )) + } + + let table = table.update( + component: TableComponent( + theme: environment.theme, + items: tableItems + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude), + transition: context.transition + ) + context.add(table + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + originY += table.size.height + 23.0 + + if component.valueInfo.listedCount != nil || component.valueInfo.fragmentListedCount != nil { + originY += 5.0 + } + + if let listedCount = component.valueInfo.listedCount, let giftIconSubject { + let telegramSaleButton = telegramSaleButton.update( + component: PlainButtonComponent( + content: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "count", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + )), + AnyComponentWithIdentity(id: "spacing", component: AnyComponent( + Rectangle(color: .clear, width: 8.0, height: 1.0) + )), + AnyComponentWithIdentity(id: "icon", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: giftIconSubject, + mode: .buttonIcon + ) + )), + AnyComponentWithIdentity(id: "label", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnTelegram)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + )), + AnyComponentWithIdentity(id: "arrow", component: AnyComponent( + BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor) + )) + ], spacing: 0.0) + ), + action: { [weak state] in + guard let state, let genericGift else { + return + } + state.openGiftResale(gift: genericGift) + }, + animateScale: false + ), + environment: {}, + availableSize: context.availableSize, + transition: .immediate + ) + context.add(telegramSaleButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + telegramSaleButton.size.height / 2.0)) + ) + originY += telegramSaleButton.size.height + originY += 12.0 + } + + if let listedCount = component.valueInfo.fragmentListedCount, let fragmentListedUrl = component.valueInfo.fragmentListedUrl, let giftIconSubject { + if component.valueInfo.listedCount != nil { + originY += 18.0 + } + + let fragmentSaleButton = fragmentSaleButton.update( + component: PlainButtonComponent( + content: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "count", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(listedCount, dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + )), + AnyComponentWithIdentity(id: "spacing", component: AnyComponent( + Rectangle(color: .clear, width: 8.0, height: 1.0) + )), + AnyComponentWithIdentity(id: "icon", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: giftIconSubject, + mode: .buttonIcon + ) + )), + AnyComponentWithIdentity(id: "label", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnFragment)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + )), + AnyComponentWithIdentity(id: "arrow", component: AnyComponent( + BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor) + )) + ], spacing: 0.0) + ), + action: { [weak state] in + state?.openGiftFragmentResale(url: fragmentListedUrl) + }, + animateScale: false + ), + environment: {}, + availableSize: context.availableSize, + transition: .immediate + ) + context.add(fragmentSaleButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + fragmentSaleButton.size.height / 2.0)) + ) + originY += fragmentSaleButton.size.height + originY += 12.0 + } + + context.add(buttons + .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - 16.0 - buttons.size.width / 2.0, y: 28.0)) + ) + + let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom + return CGSize(width: context.availableSize.width, height: originY + 5.0 + effectiveBottomInset) + } + } +} + +final class GiftValueSheetComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gift: StarGift + let valueInfo: StarGift.UniqueGift.ValueInfo + + init( + context: AccountContext, + gift: StarGift, + valueInfo: StarGift.UniqueGift.ValueInfo + ) { + self.context = context + self.gift = gift + self.valueInfo = valueInfo + } + + static func ==(lhs: GiftValueSheetComponent, rhs: GiftValueSheetComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.gift != rhs.gift { + return false + } + if lhs.valueInfo != rhs.valueInfo { + return false + } + return true + } + + static var body: Body { + let sheet = Child(SheetComponent.self) + let animateOut = StoredActionSlot(Action.self) + + let sheetExternalState = SheetComponent.ExternalState() + + return { context in + let environment = context.environment[EnvironmentType.self] + let controller = environment.controller + + let sheet = sheet.update( + component: SheetComponent( + content: AnyComponent(GiftValueSheetContent( + context: context.component.context, + gift: context.component.gift, + valueInfo: context.component.valueInfo, + animateOut: animateOut, + getController: controller + )), + backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, + clipsContent: true, + autoAnimateOut: false, + externalState: sheetExternalState, + animateOut: animateOut, + onPan: { + if let controller = controller() as? GiftValueScreen { + controller.dismissAllTooltips() + } + }, + willDismiss: { + } + ), + environment: { + environment + SheetComponentEnvironment( + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + if animated { + if let controller = controller() as? GiftValueScreen { + controller.dismissAllTooltips() + animateOut.invoke(Action { _ in + controller.dismiss(completion: nil) + }) + } + } else { + if let controller = controller() as? GiftValueScreen { + controller.dismissAllTooltips() + controller.dismiss(completion: nil) + } + } + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + if let controller = controller(), !controller.automaticallyControlPresentationContextLayout { + var sideInset: CGFloat = 0.0 + var bottomInset: CGFloat = max(environment.safeInsets.bottom, sheetExternalState.contentHeight) + if case .regular = environment.metrics.widthClass { + sideInset = floor((context.availableSize.width - 430.0) / 2.0) - 12.0 + bottomInset = (context.availableSize.height - sheetExternalState.contentHeight) / 2.0 + sheetExternalState.contentHeight + } + + let layout = ContainerViewLayout( + size: context.availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: max(sideInset, environment.safeInsets.left), bottom: 0.0, right: max(sideInset, environment.safeInsets.right)), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition) + } + + return context.availableSize + } + } +} + +final class GiftValueScreen: ViewControllerComponentContainer { + private let context: AccountContext + private let gift: StarGift + private let valueInfo: StarGift.UniqueGift.ValueInfo + + public init( + context: AccountContext, + gift: StarGift, + valueInfo: StarGift.UniqueGift.ValueInfo + ) { + self.context = context + self.gift = gift + self.valueInfo = valueInfo + + super.init( + context: context, + component: GiftValueSheetComponent( + context: context, + gift: gift, + valueInfo: valueInfo + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default + ) + + self.navigationPresentation = .flatModal + self.automaticallyControlPresentationContextLayout = false + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + public override func viewDidLoad() { + super.viewDidLoad() + + self.view.disablesInteractiveModalDismiss = true + } + + public override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.dismissAllTooltips() + } + + public func dismissAnimated() { + self.dismissAllTooltips() + + if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { + view.dismissAnimated() + } + } + + fileprivate func dismissAllTooltips() { + self.window?.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + if let controller = controller as? UndoOverlayController { + controller.dismiss() + } + }) + self.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + if let controller = controller as? UndoOverlayController { + controller.dismiss() + } + return true + }) + } +} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 121b2f7bf7..c56e3120ae 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -28,13 +28,15 @@ import ConfettiEffect import PlainButtonComponent import CheckComponent import TooltipUI -import GiftAnimationComponent import LottieComponent import ContextUI import TelegramNotices import PremiumLockButtonSubtitleComponent import StarsBalanceOverlayComponent import BalanceNeededScreen +import GiftItemComponent +import GiftAnimationComponent +import ChatThemeScreen private final class GiftViewSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -74,6 +76,9 @@ private final class GiftViewSheetContent: CombinedComponent { private let context: AccountContext private(set) var subject: GiftViewScreen.Subject + var justUpgraded = false + var revealedAttributes = Set() + var revealedNumberDigits: Int = 0 private let getController: () -> ViewController? @@ -90,7 +95,6 @@ private final class GiftViewSheetContent: CombinedComponent { var peerMap: [EnginePeer.Id: EnginePeer] = [:] var starGiftsMap: [Int64: StarGift.Gift] = [:] - var cachedCircleImage: UIImage? var cachedStarImage: (UIImage, PresentationTheme)? var cachedSmallStarImage: (UIImage, PresentationTheme)? var cachedSubtitleStarImage: (UIImage, PresentationTheme)? @@ -101,7 +105,11 @@ private final class GiftViewSheetContent: CombinedComponent { var cachedHiddenImage: (UIImage, PresentationTheme)? var inProgress = false + var canSkip = false + var testUpgradeAnimation = !"".isEmpty + + var nextGiftToUpgrade: ProfileGiftsContext.State.StarGift? var inUpgradePreview = false var upgradeForm: BotPaymentForm? var upgradeFormDisposable: Disposable? @@ -207,11 +215,36 @@ private final class GiftViewSheetContent: CombinedComponent { } }) } + + if self.testUpgradeAnimation { + if gift.giftId != 0 { + self.sampleDisposable.add((context.engine.payments.starGiftUpgradePreview(giftId: gift.giftId) + |> deliverOnMainQueue).start(next: { [weak self] attributes in + guard let self else { + return + } + self.sampleGiftAttributes = attributes + + for attribute in attributes { + switch attribute { + case let .model(_, file, _): + self.sampleDisposable.add(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + case let .pattern(_, file, _): + self.sampleDisposable.add(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + default: + break + } + } + + self.updated() + })) + } + } } else if case let .generic(gift) = arguments.gift { if let releasedBy = gift.releasedBy { peerIds.append(releasedBy) } - if arguments.canUpgrade || arguments.upgradeStars != nil { + if arguments.canUpgrade || arguments.upgradeStars != nil || arguments.prepaidUpgradeHash != nil { self.sampleDisposable.add((context.engine.payments.starGiftUpgradePreview(giftId: gift.id) |> deliverOnMainQueue).start(next: { [weak self] attributes in guard let self else { @@ -564,7 +597,7 @@ private final class GiftViewSheetContent: CombinedComponent { } if let convertToStars = controller?.convertToStars { - convertToStars() + convertToStars(reference) } else { let _ = (self.context.engine.payments.convertStarGift(reference: reference) |> deliverOnMainQueue).startStandalone() @@ -631,6 +664,43 @@ private final class GiftViewSheetContent: CombinedComponent { controller.push(introController) } + private var isOpeningValue = false + func openValue() { + guard let controller = self.getController(), let gift = self.subject.arguments?.gift, case let .unique(uniqueGift) = gift, !self.isOpeningValue else { + return + } + self.isOpeningValue = true + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let _ = (self.context.engine.payments.getUniqueStarGiftValueInfo(slug: uniqueGift.slug) + |> deliverOnMainQueue).start(next: { [weak self] valueInfo in + guard let self else { + return + } + Queue.mainQueue().after(0.2) { + self.isOpeningValue = false + } + if let valueInfo { + let valueController = GiftValueScreen(context: self.context, gift: gift, valueInfo: valueInfo) + controller.push(valueController) + } else { + guard let controller = self.getController() as? GiftViewScreen else { + return + } + let alertController = textAlertController( + context: self.context, + title: nil, + text: presentationData.strings.Login_UnknownError, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ], + parseMarkdown: true + ) + controller.present(alertController, in: .window(.root)) + } + }) + } + func sendGift(peerId: EnginePeer.Id) { guard let controller = self.getController() else { return @@ -734,6 +804,83 @@ private final class GiftViewSheetContent: CombinedComponent { controller.present(shareController, in: .window(.root)) } + func setAsGiftTheme() { + guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, let navigationController = controller.navigationController as? NavigationController, case let .unique(gift) = arguments.gift else { + return + } + + let context = self.context + + let themePeerId = Promise() + themePeerId.set( + .single(gift.themePeerId) + |> then( + context.engine.payments.getUniqueStarGift(slug: gift.slug) + |> map { gift in + return gift?.themePeerId + } + ) + ) + + let peerController = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [.user(.init(isBot: false, isPremium: nil))], hasContactSelector: false, hasCreation: false)) + peerController.peerSelected = { [weak peerController, weak navigationController] peer, _ in + if let navigationController { + let proceed = { + let _ = context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil, forBoth: true).startStandalone() + let _ = context.engine.themes.setChatTheme(peerId: peer.id, chatTheme: .gift(.unique(gift), [])).startStandalone() + + peerController?.dismiss() + + context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + chatController: nil, + context: context, + chatLocation: .peer(peer), + subject: nil, + botStart: nil, + updateTextInputState: nil, + keepStack: .always, + useExisting: true, + purposefulAction: nil, + scrollToEndIfExists: false, + activateMessageSearch: nil, + animated: true + )) + } + + let _ = (themePeerId.get() + |> deliverOnMainQueue + |> take(1)).start(next: { [weak navigationController] themePeerId in + if let themePeerId, themePeerId != peer.id { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: themePeerId)) + |> deliverOnMainQueue).start(next: { [weak navigationController] peer in + guard let peer else { + proceed() + return + } + let controller = giftThemeTransferAlertController( + context: context, + gift: gift, + previousPeer: peer, + commit: { + proceed() + } + ) + (navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) + }) + } else { + proceed() + } + }) + } + } + self.dismiss(animated: true) + + Queue.mainQueue().after(0.4) { + navigationController.pushViewController(peerController) + } + } + func transferGift() { guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, case let .unique(gift) = arguments.gift, let reference = arguments.reference, let transferStars = arguments.transferStars else { return @@ -821,7 +968,7 @@ private final class GiftViewSheetContent: CombinedComponent { return } - let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))" + let giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: presentationData.dateTimeFormat))" let reference = arguments.reference ?? .slug(slug: gift.slug) if let resellStars = gift.resellAmounts?.first, resellStars.amount.value > 0, !update { @@ -1065,6 +1212,16 @@ private final class GiftViewSheetContent: CombinedComponent { self?.shareGift() }))) + + if gift.flags.contains(.isThemeAvailable) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_SetAsTheme, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + c?.dismiss(completion: nil) + + self?.setAsGiftTheme() + }))) + } if let _ = arguments.transferStars { if case let .channel(channel) = peer, !channel.flags.contains(.isCreator) { @@ -1114,6 +1271,7 @@ private final class GiftViewSheetContent: CombinedComponent { if animated { controller.dismissAllTooltips() controller.dismissBalanceOverlay() + controller.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.3).withUserData(ViewControllerComponentContainer.AnimateOutTransition())) self.animateOut.invoke(Action { [weak controller] _ in controller?.dismiss(completion: nil) }) @@ -1155,15 +1313,12 @@ private final class GiftViewSheetContent: CombinedComponent { } func requestUpgradePreview() { - guard let arguments = self.subject.arguments, arguments.canUpgrade || arguments.upgradeStars != nil else { - return - } self.context.starsContext?.load(force: false) self.inUpgradePreview = true self.updated(transition: .spring(duration: 0.4)) - if let controller = self.getController() as? GiftViewScreen { + if let controller = self.getController() as? GiftViewScreen, self.upgradeForm != nil { controller.showBalance = true } } @@ -1203,7 +1358,7 @@ private final class GiftViewSheetContent: CombinedComponent { return } - let giftTitle = "\(uniqueGift.title) #\(uniqueGift.number)" + let giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: presentationData.dateTimeFormat))" let recipientPeerId = self.recipientPeerId ?? self.context.account.peerId let action: (CurrencyAmount.Currency) -> Void = { currency in @@ -1482,13 +1637,90 @@ private final class GiftViewSheetContent: CombinedComponent { } } + func skipAnimation() { + guard let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift else { + return + } + self.canSkip = false + self.revealedNumberDigits = "\(uniqueGift.number)".count + self.revealedAttributes.insert(.backdrop) + self.revealedAttributes.insert(.pattern) + self.revealedAttributes.insert(.model) + + self.updated(transition: .easeInOut(duration: 0.2)) + } + func commitUpgrade() { - guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { + let duration = Double.random(in: 0.85 ..< 2.25) + let firstFraction = Double.random(in: 0.2 ..< 0.4) + let secondFraction = Double.random(in: 0.2 ..< 0.4) + let thirdFraction = 1.0 - firstFraction - secondFraction + let firstDuration = duration * firstFraction + let secondDuration = duration * secondFraction + let thirdDuration = duration * thirdFraction + + if self.testUpgradeAnimation, let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift { + self.inProgress = true + self.updated() + + if let controller = self.getController() as? GiftViewScreen { + controller.showBalance = false + } + + Queue.mainQueue().after(0.5, { + self.canSkip = true + self.updated(transition: .immediate) + + self.inProgress = false + self.inUpgradePreview = false + + self.justUpgraded = true + self.revealedNumberDigits = -1 + + for i in 0 ..< "\(uniqueGift.number)".count { + Queue.mainQueue().after(0.2 + Double(i) * 0.3) { + self.revealedNumberDigits += 1 + self.updated(transition: .immediate) + } + } + + Queue.mainQueue().after(firstDuration) { + self.revealedAttributes.insert(.backdrop) + self.updated(transition: .immediate) + + Queue.mainQueue().after(secondDuration) { + self.revealedAttributes.insert(.pattern) + self.updated(transition: .immediate) + + Queue.mainQueue().after(thirdDuration) { + self.revealedAttributes.insert(.model) + self.updated(transition: .immediate) + + Queue.mainQueue().after(0.55) { + self.canSkip = false + self.updated(transition: .easeInOut(duration: 0.2)) + } + + Queue.mainQueue().after(0.6) { + if let controller = self.getController() as? GiftViewScreen { + controller.animateSuccess() + } + } + } + } + } + + self.updated(transition: .spring(duration: 0.4)) + }) return } - let proceed: (Int64?) -> Void = { formId in - guard let controller = self.getController() as? GiftViewScreen else { + guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { + return + } + + let proceed: (Int64?) -> Void = { [weak self] formId in + guard let self, let controller = self.getController() as? GiftViewScreen else { return } self.inProgress = true @@ -1499,8 +1731,11 @@ private final class GiftViewSheetContent: CombinedComponent { let context = self.context let upgradeGiftImpl: ((Int64?, Bool) -> Signal) if let upgradeGift = controller.upgradeGift { + guard let reference = arguments.reference else { + return + } upgradeGiftImpl = { formId, keepOriginalInfo in - return upgradeGift(formId, keepOriginalInfo) + return upgradeGift(formId, reference, keepOriginalInfo) |> afterCompleted { if formId != nil { context.starsContext?.load(force: true) @@ -1526,11 +1761,56 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self, let controller = self.getController() as? GiftViewScreen else { return } + self.canSkip = true + self.updated(transition: .immediate) + self.inProgress = false self.inUpgradePreview = false + if let reference = arguments.reference { + controller.upgradedGiftReferences.insert(reference) + self.nextGiftToUpgrade = controller.nextUpgradableGift + } + + self.justUpgraded = true + self.revealedNumberDigits = -1 + + if case let .unique(uniqueGift) = result.gift { + for i in 0 ..< "\(uniqueGift.number)".count { + Queue.mainQueue().after(0.2 + Double(i) * 0.3) { + self.revealedNumberDigits += 1 + self.updated(transition: .immediate) + } + } + } + + Queue.mainQueue().after(firstDuration) { + self.revealedAttributes.insert(.backdrop) + self.updated(transition: .immediate) + + Queue.mainQueue().after(secondDuration) { + self.revealedAttributes.insert(.pattern) + self.updated(transition: .immediate) + + Queue.mainQueue().after(thirdDuration) { + self.revealedAttributes.insert(.model) + self.updated(transition: .immediate) + + Queue.mainQueue().after(0.55) { + self.canSkip = false + self.updated(transition: .easeInOut(duration: 0.2)) + } + + Queue.mainQueue().after(0.6) { + if let controller = self.getController() as? GiftViewScreen { + controller.animateSuccess() + } + } + } + } + } + self.subject = .profileGift(peerId, result) - controller.animateSuccess() self.updated(transition: .spring(duration: 0.4)) Queue.mainQueue().after(0.5) { @@ -1576,8 +1856,101 @@ private final class GiftViewSheetContent: CombinedComponent { } } +// if let controller = self.getController() as? GiftViewScreen { +// controller.showBalance = true +// } + } + + func commitPrepaidUpgrade() { + guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let prepaidUpgradeHash = arguments.prepaidUpgradeHash, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { + return + } + guard case let .generic(gift) = arguments.gift else { + return + } + guard let gift = self.starGiftsMap[gift.id], let price = gift.upgradeStars else { + return + } + let context = self.context + let proceed: () -> Void = { [weak self, weak starsContext] in + guard let self else { + return + } + self.inProgress = true + self.updated() + + let source: BotPaymentInvoiceSource = .starGiftPrepaidUpgrade(peerId: peerId, hash: prepaidUpgradeHash) + let signal = context.engine.payments.fetchBotPaymentForm(source: source, themeParams: nil) + |> map(Optional.init) + |> `catch` { _ in + return .single(nil) + } + |> mapToSignal { paymentForm in + if let paymentForm { + return context.engine.payments.sendStarsPaymentForm(formId: paymentForm.id, source: source) + } else { + return .fail(.generic) + } + } + + self.upgradeDisposable = (signal + |> deliverOnMainQueue).start(next: { [weak self, weak starsContext] result in + guard let self else { + return + } + Queue.mainQueue().after(0.5) { + starsContext?.load(force: true) + } + + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + self.openPeer(peer, gifts: false, dismiss: true) + }) + }, error: { _ in + + }) + } + + if starsState.balance < StarsAmount(value: price, nanos: 0) { + let _ = (self.optionsPromise.get() + |> filter { $0 != nil } + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] options in + guard let self, let controller = self.getController() else { + return + } + let purchaseController = self.context.sharedContext.makeStarsPurchaseScreen( + context: self.context, + starsContext: starsContext, + options: options ?? [], + purpose: .upgradeStarGift(requiredStars: price), + completion: { [weak self, weak starsContext] stars in + guard let self, let starsContext else { + return + } + self.inProgress = true + self.updated() + + starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) + let _ = (starsContext.onUpdate + |> deliverOnMainQueue).start(next: { + proceed() + }) + } + ) + controller.push(purchaseController) + }) + } else { + proceed() + } + } + + func switchToNextUpgradable() { if let controller = self.getController() as? GiftViewScreen { - controller.showBalance = true + controller.switchToNextUpgradable() } } } @@ -1596,6 +1969,7 @@ private final class GiftViewSheetContent: CombinedComponent { let descriptionButton = Child(PlainButtonComponent.self) let description = Child(MultilineTextComponent.self) + let animatedDescription = Child(HStack.self) let transferButton = Child(PlainButtonComponent.self) let wearButton = Child(PlainButtonComponent.self) @@ -1612,6 +1986,7 @@ private final class GiftViewSheetContent: CombinedComponent { let table = Child(TableComponent.self) let additionalText = Child(MultilineTextComponent.self) let button = Child(ButtonComponent.self) + let upgradeNextButton = Child(PlainButtonComponent.self) let upgradeTitle = Child(MultilineTextComponent.self) let upgradeDescription = Child(BalancedTextComponent.self) @@ -1662,6 +2037,7 @@ private final class GiftViewSheetContent: CombinedComponent { var isChannelGift = false var isMyUniqueGift = false var releasedByPeer: EnginePeer? + var canGiftUpgrade = false if case let .soldOutGift(gift) = subject { animationFile = gift.file @@ -1704,13 +2080,14 @@ private final class GiftViewSheetContent: CombinedComponent { uniqueGift = gift } savedToProfile = arguments.savedToProfile - if let reference = arguments.reference, case .peer = reference { + if let reference = arguments.reference, case let .peer(peerId, _) = reference, peerId.namespace == Namespaces.Peer.CloudChannel { isChannelGift = true incoming = true } else { incoming = arguments.incoming || arguments.peerId == component.context.account.peerId } nameHidden = arguments.nameHidden + canGiftUpgrade = arguments.prepaidUpgradeHash != nil isSelfGift = arguments.messageId?.peerId == component.context.account.peerId @@ -1736,7 +2113,7 @@ private final class GiftViewSheetContent: CombinedComponent { if !canUpgrade, let gift = state.starGiftsMap[giftId], let _ = gift.upgradeStars { canUpgrade = true } - + var showUpgradePreview = false if state.inUpgradePreview, let _ = state.sampleGiftAttributes { showUpgradePreview = true @@ -1773,6 +2150,11 @@ private final class GiftViewSheetContent: CombinedComponent { } }, morePressed: { [weak state] node, gesture in + if state?.testUpgradeAnimation == true { + state?.requestUpgradePreview() + return + } + state?.openMore(node: node, gesture: gesture) } ), @@ -1784,7 +2166,7 @@ private final class GiftViewSheetContent: CombinedComponent { let headerHeight: CGFloat let headerSubject: GiftCompositionComponent.Subject? - if let uniqueGift { + if let uniqueGift, !state.inUpgradePreview { if showWearPreview { headerHeight = 200.0 } else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId || isChannelGift { @@ -1792,7 +2174,7 @@ private final class GiftViewSheetContent: CombinedComponent { } else { headerHeight = 240.0 } - headerSubject = .unique(uniqueGift) + headerSubject = .unique(state.justUpgraded ? state.sampleGiftAttributes : nil, uniqueGift) } else if state.inUpgradePreview, let attributes = state.sampleGiftAttributes { headerHeight = 258.0 headerSubject = .preview(attributes) @@ -1838,8 +2220,7 @@ private final class GiftViewSheetContent: CombinedComponent { if case .wearPreview = component.subject { giftTitle = uniqueGift.title } else { - - giftTitle = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))" + giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))" } let wearTitle = wearTitle.update( @@ -1895,6 +2276,8 @@ private final class GiftViewSheetContent: CombinedComponent { animationScale = 0.19 } + var headerComponents: [() -> Void] = [] + if let headerSubject { let animation = animation.update( component: GiftCompositionComponent( @@ -1904,17 +2287,20 @@ private final class GiftViewSheetContent: CombinedComponent { animationOffset: animationOffset, animationScale: animationScale, displayAnimationStars: showWearPreview, + revealedAttributes: state.revealedAttributes, externalState: giftCompositionExternalState, - requestUpdate: { [weak state] in - state?.updated() + requestUpdate: { [weak state] transition in + state?.updated(transition: transition) } ), availableSize: CGSize(width: context.availableSize.width, height: headerHeight), transition: context.transition ) - context.add(animation - .position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight / 2.0)) - ) + headerComponents.append({ + context.add(animation + .position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight / 2.0)) + ) + }) } originY += headerHeight @@ -1937,11 +2323,13 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: 100.0, height: 100.0), transition: context.transition ) - context.add(wearAvatar - .position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) + headerComponents.append({ + context.add(wearAvatar + .position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + }) } let wearPeerStatus = wearPeerStatus.update( @@ -1960,21 +2348,19 @@ private final class GiftViewSheetContent: CombinedComponent { transition: .immediate ) - context.add(wearPeerNameChild - .position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 144.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) - context.add(wearPeerStatus - .position(CGPoint(x: context.availableSize.width / 2.0, y: 174.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) - originY += 18.0 - originY += 28.0 - originY += 18.0 - originY += 20.0 - originY += 24.0 + headerComponents.append({ + context.add(wearPeerNameChild + .position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 144.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + context.add(wearPeerStatus + .position(CGPoint(x: context.availableSize.width / 2.0, y: 174.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) + originY += 108.0 let textColor = theme.actionSheet.primaryTextColor let secondaryTextColor = theme.actionSheet.secondaryTextColor @@ -2030,11 +2416,13 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - perksSideInset * 2.0, height: 10000.0), transition: context.transition ) + context.add(wearPerks .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + wearPerks.size.height / 2.0)) .appear(.default(alpha: true)) .disappear(.default(alpha: true)) ) + originY += wearPerks.size.height originY += 16.0 } else if showUpgradePreview { @@ -2043,9 +2431,23 @@ private final class GiftViewSheetContent: CombinedComponent { let uniqueText: String let transferableText: String let tradableText: String - if case let .upgradePreview(_, name) = component.subject { + if !incoming, case let .profileGift(peerId, _) = subject, let peer = state.peerMap[peerId] { + var peerName = peer.compactDisplayTitle + if peerName.count > 22 { + peerName = "\(peerName.prefix(22))…" + } + title = environment.strings.Gift_Upgrade_GiftTitle + description = environment.strings.Gift_Upgrade_GiftDescription(peerName).string + uniqueText = strings.Gift_Upgrade_Unique_GiftDescription(peerName).string + transferableText = strings.Gift_Upgrade_Transferable_GiftDescription(peerName).string + tradableText = strings.Gift_Upgrade_Tradable_GiftDescription(peerName).string + } else if case let .upgradePreview(_, peerName) = component.subject { + var peerName = peerName + if peerName.count > 22 { + peerName = "\(peerName.prefix(22))…" + } title = environment.strings.Gift_Upgrade_IncludeTitle - description = environment.strings.Gift_Upgrade_IncludeDescription(name).string + description = environment.strings.Gift_Upgrade_IncludeDescription(peerName).string uniqueText = strings.Gift_Upgrade_Unique_IncludeDescription transferableText = strings.Gift_Upgrade_Transferable_IncludeDescription tradableText = strings.Gift_Upgrade_Tradable_IncludeDescription @@ -2076,31 +2478,34 @@ private final class GiftViewSheetContent: CombinedComponent { text: .plain(NSAttributedString( string: description, font: Font.regular(13.0), - textColor: vibrantColor, + textColor: .white, paragraphAlignment: .center )), horizontalAlignment: .center, maximumNumberOfLines: 5, - lineSpacing: 0.2 + lineSpacing: 0.2, + tintColor: vibrantColor ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), - transition: .immediate + transition: context.transition ) let spacing: CGFloat = 6.0 let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height - context.add(upgradeTitle - .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0))) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) - - context.add(upgradeDescription - .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0))) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) + headerComponents.append({ + context.add(upgradeTitle + .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0))) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + + context.add(upgradeDescription + .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0))) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) originY += 24.0 let textColor = theme.actionSheet.primaryTextColor @@ -2167,6 +2572,7 @@ private final class GiftViewSheetContent: CombinedComponent { if case .upgradePreview = component.subject { + } else if !incoming { } else { let checkTheme = CheckComponent.Theme( backgroundColor: theme.list.itemCheckColors.fillColor, @@ -2223,10 +2629,10 @@ private final class GiftViewSheetContent: CombinedComponent { var hasDescriptionButton = false if let uniqueGift { titleString = uniqueGift.title - descriptionText = "\(strings.Gift_Unique_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))" + descriptionText = "\(strings.Gift_Unique_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))" if let releasedBy = uniqueGift.releasedBy, let peer = state.peerMap[releasedBy], let addressName = peer.addressName { - descriptionText = strings.Gift_Unique_CollectibleBy("#\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))", "[@\(addressName)]()").string + descriptionText = strings.Gift_Unique_CollectibleBy("#\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))", "[@\(addressName)]()").string hasDescriptionButton = true releasedByPeer = peer } @@ -2293,11 +2699,13 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), transition: .immediate ) - context.add(title - .position(CGPoint(x: context.availableSize.width / 2.0, y: uniqueGift != nil ? 190.0 : 173.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) + headerComponents.append({ + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: uniqueGift != nil ? 190.0 : 173.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) var descriptionOffset: CGFloat = 0.0 if let subtitleString { @@ -2333,14 +2741,16 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), transition: .immediate ) - context.add(subtitle - .position(CGPoint(x: context.availableSize.width / 2.0, y: uniqueGift != nil ? 210.0 : 196.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) + headerComponents.append({ + context.add(subtitle + .position(CGPoint(x: context.availableSize.width / 2.0, y: uniqueGift != nil ? 210.0 : 196.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) descriptionOffset += subtitle.size.height } - + var useDescriptionTint = false if !descriptionText.isEmpty { var linkColor = theme.actionSheet.controlAccentColor if hasDescriptionButton { @@ -2356,18 +2766,20 @@ private final class GiftViewSheetContent: CombinedComponent { let textFont: UIFont let textColor: UIColor + if let _ = uniqueGift { textFont = Font.regular(13.0) if hasDescriptionButton { textColor = vibrantColor.mixedWith(UIColor.white, alpha: 0.4) } else { textColor = vibrantColor + useDescriptionTint = true } } else { textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0) textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor } - let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: useDescriptionTint ? .white : textColor), bold: MarkdownAttributeSet(font: textFont, textColor: useDescriptionTint ? .white : textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) }) @@ -2382,59 +2794,126 @@ private final class GiftViewSheetContent: CombinedComponent { attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string)) } - let description = description.update( - component: MultilineTextComponent( - text: .plain(attributedString), - horizontalAlignment: .center, - maximumNumberOfLines: 5, - lineSpacing: 0.2, - highlightColor: linkColor.withAlphaComponent(0.1), - highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), - highlightAction: { attributes in - if !hasDescriptionButton, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + var descriptionSize = CGSize() + if state.justUpgraded { + var items: [AnyComponentWithIdentity] = [ + AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: "\(strings.Gift_Unique_Collectible) #", font: textFont, color: .white, tintColor: textColor))) + ] + + let numberFont = Font.with(size: 13.0, traits: .monospacedNumbers) + let spinningItems: [AnyComponentWithIdentity] = [ + AnyComponentWithIdentity(id: "0", component: AnyComponent(Text(text: "0", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "1", component: AnyComponent(Text(text: "1", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "2", component: AnyComponent(Text(text: "2", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "3", component: AnyComponent(Text(text: "3", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "4", component: AnyComponent(Text(text: "4", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "5", component: AnyComponent(Text(text: "5", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "6", component: AnyComponent(Text(text: "6", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "7", component: AnyComponent(Text(text: "7", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "8", component: AnyComponent(Text(text: "8", font: numberFont, color: textColor))), + AnyComponentWithIdentity(id: "9", component: AnyComponent(Text(text: "9", font: numberFont, color: textColor))) + ] + if let numberValue = uniqueGift?.number { + let numberString = formatCollectibleNumber(numberValue, dateTimeFormat: environment.dateTimeFormat) + var i = 0 + var index = 0 + for c in numberString { + let s = String(c) + if s == "\u{00A0}" { + items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(Text(text: s, font: textFont, color: .white, tintColor: textColor))) + ) + } else if ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].contains(s) { + items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(SlotsComponent( + item: AnyComponent(Text(text: String(c), font: numberFont, color: .white)), + items: spinningItems, + isAnimating: index > state.revealedNumberDigits, + tintColor: textColor, + verticalOffset: -1.0 - UIScreenPixel, + motionBlur: false, + size: CGSize(width: 8.0, height: 14.0)))) + ) + index += 1 } else { - return nil - } - }, - tapAction: { [weak state] attributes, _ in - if !hasDescriptionButton, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { - state?.openStarsIntro() + items.append(AnyComponentWithIdentity(id: "c\(i)", component: AnyComponent(Text(text: s, font: numberFont, color: .white, tintColor: textColor))) + ) } + i += 1 } - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), - transition: .immediate - ) - context.add(description - .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) - ) - - if hasDescriptionButton { - let descriptionButton = descriptionButton.update( - component: PlainButtonComponent( - content: AnyComponent( - RoundedRectangle(color: UIColor.white.withAlphaComponent(0.15), cornerRadius: 9.5) - ), - effectAlignment: .center, - action: { [weak state] in - if let releasedByPeer { - state?.openPeer(releasedByPeer) + } + let animatedDescription = animatedDescription.update( + component: HStack(items, spacing: 0.0), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), + transition: context.transition + ) + descriptionSize = animatedDescription.size + headerComponents.append({ + context.add(animatedDescription + .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + animatedDescription.size.height / 2.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) + } else { + let description = description.update( + component: MultilineTextComponent( + text: .plain(attributedString), + horizontalAlignment: .center, + maximumNumberOfLines: 5, + lineSpacing: 0.2, + tintColor: useDescriptionTint ? textColor : nil, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), + highlightAction: { attributes in + if !hasDescriptionButton, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil } }, - animateScale: false + tapAction: { [weak state] attributes, _ in + if !hasDescriptionButton, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + state?.openStarsIntro() + } + } ), - environment: {}, - availableSize: CGSize(width: description.size.width + 18.0, height: 19.0), - transition: .immediate - ) - context.add(descriptionButton - .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0 - 1.0)) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), + transition: context.transition ) + descriptionSize = description.size + headerComponents.append({ + context.add(description + .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) + + if hasDescriptionButton { + let descriptionButton = descriptionButton.update( + component: PlainButtonComponent( + content: AnyComponent( + RoundedRectangle(color: UIColor.white.withAlphaComponent(0.15), cornerRadius: 9.5) + ), + effectAlignment: .center, + action: { [weak state] in + if let releasedByPeer { + state?.openPeer(releasedByPeer) + } + }, + animateScale: false + ), + environment: {}, + availableSize: CGSize(width: description.size.width + 18.0, height: 19.0), + transition: .immediate + ) + headerComponents.append({ + context.add(descriptionButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0 - 1.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) + } } originY += descriptionOffset @@ -2442,7 +2921,7 @@ private final class GiftViewSheetContent: CombinedComponent { if uniqueGift != nil { originY += 16.0 } else { - originY += description.size.height + 21.0 + originY += descriptionSize.height + 21.0 if soldOut { originY -= 7.0 } @@ -2459,7 +2938,11 @@ private final class GiftViewSheetContent: CombinedComponent { if incoming { hiddenDescription = text != nil ? strings.Gift_View_NameAndMessageHidden : strings.Gift_View_NameHidden } else if let peerId = subject.arguments?.peerId, let peer = state.peerMap[peerId], subject.arguments?.fromPeerId != nil { - hiddenDescription = text != nil ? strings.Gift_View_Outgoing_NameAndMessageHidden(peer.compactDisplayTitle).string : strings.Gift_View_Outgoing_NameHidden(peer.compactDisplayTitle).string + var peerName = peer.compactDisplayTitle + if peerName.count > 30 { + peerName = "\(peerName.prefix(30))…" + } + hiddenDescription = text != nil ? strings.Gift_View_Outgoing_NameAndMessageHidden(peerName).string : strings.Gift_View_Outgoing_NameHidden(peerName).string } else { hiddenDescription = "" } @@ -2754,11 +3237,14 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: buttonWidth, height: buttonHeight), transition: context.transition ) - context.add(transferButton - .position(CGPoint(x: buttonOriginX + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) + let buttonPosition = buttonOriginX + buttonWidth / 2.0 + headerComponents.append({ + context.add(transferButton + .position(CGPoint(x: buttonPosition, y: headerHeight - buttonHeight / 2.0 - 16.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + }) buttonOriginX += buttonWidth + buttonSpacing } @@ -2776,7 +3262,7 @@ private final class GiftViewSheetContent: CombinedComponent { if isWearing { state.commitTakeOff() - state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_TookOff("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) + state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_TookOff("\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))").string) } else { if let controller = controller() as? GiftViewScreen { controller.dismissAllTooltips() @@ -2803,7 +3289,7 @@ private final class GiftViewSheetContent: CombinedComponent { state.requestWearPreview() } else { state.commitWear(uniqueGift) - state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_PutOn("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) + state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_PutOn("\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))").string) } }) } @@ -2814,11 +3300,14 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: buttonWidth, height: buttonHeight), transition: context.transition ) - context.add(wearButton - .position(CGPoint(x: buttonOriginX + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) + let buttonPosition = buttonOriginX + buttonWidth / 2.0 + headerComponents.append({ + context.add(wearButton + .position(CGPoint(x: buttonPosition, y: headerHeight - buttonHeight / 2.0 - 16.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + }) buttonOriginX += buttonWidth + buttonSpacing if canResell { @@ -2826,8 +3315,8 @@ private final class GiftViewSheetContent: CombinedComponent { component: PlainButtonComponent( content: AnyComponent( HeaderButtonComponent( - title: uniqueGift.resellAmounts == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist, - iconName: uniqueGift.resellAmounts == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist" + title: (uniqueGift.resellAmounts ?? []).isEmpty ? strings.Gift_View_Sell : strings.Gift_View_Unlist, + iconName: (uniqueGift.resellAmounts ?? []).isEmpty ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist" ) ), effectAlignment: .center, @@ -2839,16 +3328,19 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: buttonWidth, height: buttonHeight), transition: context.transition ) - context.add(resellButton - .position(CGPoint(x: buttonOriginX + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) + let buttonPosition = buttonOriginX + buttonWidth / 2.0 + headerComponents.append({ + context.add(resellButton + .position(CGPoint(x: buttonPosition, y: headerHeight - buttonHeight / 2.0 - 16.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + }) } } let order: [StarGift.UniqueGift.Attribute.AttributeType] = [ - .model, .backdrop, .pattern, .originalInfo + .model, .pattern, .backdrop, .originalInfo ] var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:] @@ -2859,13 +3351,15 @@ private final class GiftViewSheetContent: CombinedComponent { var hasOriginalInfo = false for type in order { if let attribute = attributeMap[type] { - let id: String + var id: String let title: String? let value: NSAttributedString let percentage: Float? let tag: AnyObject? var hasBackground = false + var otherValuesAndPercentages: [(value: String, percentage: Float)] = [] + switch attribute { case let .model(name, _, rarity): id = "model" @@ -2873,18 +3367,42 @@ private final class GiftViewSheetContent: CombinedComponent { value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) percentage = Float(rarity) * 0.1 tag = state.modelButtonTag + + if state.justUpgraded, let sampleAttributes = state.sampleGiftAttributes { + for sampleAttribute in sampleAttributes { + if case let .model(name, _, rarity) = sampleAttribute { + otherValuesAndPercentages.append((name, Float(rarity) * 0.1)) + } + } + } case let .backdrop(name, _, _, _, _, _, rarity): id = "backdrop" title = strings.Gift_Unique_Backdrop value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) percentage = Float(rarity) * 0.1 tag = state.backdropButtonTag + + if state.justUpgraded, let sampleAttributes = state.sampleGiftAttributes { + for sampleAttribute in sampleAttributes { + if case let .backdrop(name, _, _, _, _, _, rarity) = sampleAttribute { + otherValuesAndPercentages.append((name, Float(rarity) * 0.1)) + } + } + } case let .pattern(name, _, rarity): id = "pattern" title = strings.Gift_Unique_Symbol value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) percentage = Float(rarity) * 0.1 tag = state.symbolButtonTag + + if state.justUpgraded, let sampleAttributes = state.sampleGiftAttributes { + for sampleAttribute in sampleAttributes { + if case let .pattern(name, _, rarity) = sampleAttribute { + otherValuesAndPercentages.append((name, Float(rarity) * 0.1)) + } + } + } case let .originalInfo(senderPeerId, recipientPeerId, date, text, entities): id = "originalInfo" title = nil @@ -2936,6 +3454,10 @@ private final class GiftViewSheetContent: CombinedComponent { hasOriginalInfo = true } + if !otherValuesAndPercentages.isEmpty { + id += "_reel" + } + var items: [AnyComponentWithIdentity] = [] items.append( AnyComponentWithIdentity( @@ -2986,9 +3508,41 @@ private final class GiftViewSheetContent: CombinedComponent { ).tagged(tag)) )) } - let itemComponent = AnyComponent( + var itemComponent = AnyComponent( HStack(items, spacing: 4.0) ) + + if !otherValuesAndPercentages.isEmpty { + var subitems: [AnyComponentWithIdentity] = [] + var index = 0 + + for (title, percentage) in otherValuesAndPercentages { + subitems.append( + AnyComponentWithIdentity(id: "anim_\(index)", component: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: title, font: tableFont, color: tableTextColor))), + AnyComponentWithIdentity(id: "rarity", component: AnyComponent(ButtonContentComponent( + context: component.context, + text: formatPercentage(percentage), + color: theme.list.itemAccentColor + ))) + ], spacing: 4.0) + )) + ) + index += 1 + } + + itemComponent = AnyComponent( + SlotsComponent( + item: itemComponent, + items: subitems, + isAnimating: !state.revealedAttributes.contains(type), + motionBlur: false, + size: CGSize(width: 160.0, height: 18.0) + ) + ) + } + tableItems.append(.init( id: id, title: title, @@ -3007,6 +3561,35 @@ private final class GiftViewSheetContent: CombinedComponent { MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Unique_Issued("\(issuedString)/\(totalString)").string, font: tableFont, textColor: tableTextColor))) ) ), at: hasOriginalInfo ? tableItems.count - 1 : tableItems.count) + + if let valueAmount = uniqueGift.valueAmount, let valueCurrency = uniqueGift.valueCurrency { + tableItems.insert(.init( + id: "fiatValue", + title: strings.Gift_Unique_Value, + component: AnyComponent( + HStack([ + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "≈\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))) + ), + AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: component.context, + text: strings.Gift_Unique_LearnMore, + color: theme.list.itemAccentColor + )), + action: { [weak state] in + state?.openValue() + } + )) + ) + ], spacing: 4.0) + ), + insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0) + ), at: hasOriginalInfo ? tableItems.count - 1 : tableItems.count) + } } else { if case let .soldOutGift(gift) = subject, let soldOut = gift.soldOut { tableItems.append(.init( @@ -3130,23 +3713,16 @@ private final class GiftViewSheetContent: CombinedComponent { } if !soldOut && canUpgrade { - var items: [AnyComponentWithIdentity] = [] - items.append( - AnyComponentWithIdentity( - id: AnyHashable(0), - component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_View_Status_NonUnique, font: tableFont, textColor: tableTextColor)))) - ) - ) tableItems.append(.init( id: "status", title: strings.Gift_View_Status, component: AnyComponent( - HStack(items, spacing: 4.0) + MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_View_Status_NonUnique, font: tableFont, textColor: tableTextColor))) ), insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0) )) } - + if let text { let attributedText = stringWithAppliedEntities(text, entities: entities ?? [], baseColor: tableTextColor, linkColor: tableLinkColor, baseFont: tableFont, linkFont: tableFont, boldFont: tableBoldFont, italicFont: tableItalicFont, boldItalicFont: tableBoldItalicFont, fixedFont: tableMonospaceFont, blockQuoteFont: tableFont, message: nil) @@ -3180,10 +3756,19 @@ private final class GiftViewSheetContent: CombinedComponent { context.add(table .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0)) .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) + .disappear(ComponentTransition.Disappear({ view, transition, completion in + view.superview?.insertSubview(view, at: 0) + transition.setAlpha(view: view, alpha: 0.0, completion: { _ in + completion() + }) + })) ) originY += table.size.height + 23.0 } + + for component in headerComponents { + component() + } var resellAmount: CurrencyAmount? var selling = false @@ -3193,7 +3778,7 @@ private final class GiftViewSheetContent: CombinedComponent { } else { resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars }) } - if let resellAmount { + if let resellAmount, wearPeerNameChild == nil { if incoming || ownerPeerId == component.context.account.peerId { let priceButton = priceButton.update( component: PlainButtonComponent( @@ -3222,7 +3807,12 @@ private final class GiftViewSheetContent: CombinedComponent { } } - if ((incoming && !converted && !upgraded) || exported || selling) && (!showUpgradePreview && !showWearPreview) { + + var isChatTheme = false + if let controller = controller() as? GiftViewScreen, controller.openChatTheme != nil { + isChatTheme = true + } + if ((incoming && !converted && !upgraded) || exported || selling || isChatTheme) && (!showUpgradePreview && !showWearPreview) { let textFont = Font.regular(13.0) let textColor = theme.list.itemSecondaryTextColor let linkColor = theme.actionSheet.controlAccentColor @@ -3236,7 +3826,9 @@ private final class GiftViewSheetContent: CombinedComponent { var addressToOpen: String? var descriptionText: String - if let uniqueGift, selling { + if isChatTheme { + descriptionText = strings.Gift_View_OpenChatTheme + } else if let uniqueGift, selling { let ownerName: String if case let .peerId(peerId) = uniqueGift.owner { ownerName = state.peerMap[peerId]?.compactDisplayTitle ?? "" @@ -3295,7 +3887,10 @@ private final class GiftViewSheetContent: CombinedComponent { }, tapAction: { [weak state] attributes, _ in if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { - if let addressToOpen { + if isChatTheme, let controller = controller() as? GiftViewScreen { + state?.dismiss(animated: true) + controller.openChatTheme?() + } else if let addressToOpen { state?.openAddress(addressToOpen) } else { state?.updateSavedToProfile(!savedToProfile) @@ -3325,7 +3920,25 @@ private final class GiftViewSheetContent: CombinedComponent { pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) ) let buttonChild: _UpdatedChildComponent - if showWearPreview, let uniqueGift { + if state.canSkip { + buttonChild = button.update( + component: ButtonComponent( + background: buttonBackground, + content: AnyComponentWithIdentity( + id: AnyHashable("skip"), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_Skip, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + isEnabled: true, + displaysProgress: state.inProgress, + action: { [weak state] in + if let state { + state.skipAnimation() + } + }), + availableSize: buttonSize, + transition: context.transition + ) + } else if showWearPreview, let uniqueGift { let buttonContent: AnyComponentWithIdentity let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) @@ -3437,7 +4050,7 @@ private final class GiftViewSheetContent: CombinedComponent { state.dismiss(animated: true) } else { Queue.mainQueue().after(0.2) { - state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_PutOn("\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))").string) + state.showAttributeInfo(tag: state.statusTag, text: strings.Gift_View_PutOn("\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))").string) } } } @@ -3451,7 +4064,11 @@ private final class GiftViewSheetContent: CombinedComponent { state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: theme.list.itemCheckColors.foregroundColor)!, theme) } var upgradeString = strings.Gift_Upgrade_Upgrade - if let upgradeForm = state.upgradeForm, let price = upgradeForm.invoice.prices.first?.amount { + if !incoming { + if let gift = state.starGiftsMap[giftId], let upgradeStars = gift.upgradeStars { + upgradeString = strings.Gift_Upgrade_GiftUpgrade(" # \(upgradeStars)").string + } + } else if let upgradeForm = state.upgradeForm, let price = upgradeForm.invoice.prices.first?.amount { upgradeString += " # \(presentationStringsFormattedNumber(Int32(price), environment.dateTimeFormat.groupingSeparator))" } let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : upgradeString @@ -3472,7 +4089,11 @@ private final class GiftViewSheetContent: CombinedComponent { isEnabled: true, displaysProgress: state.inProgress, action: { [weak state] in - state?.commitUpgrade() + if canGiftUpgrade { + state?.commitPrepaidUpgrade() + } else { + state?.commitUpgrade() + } }), availableSize: buttonSize, transition: context.transition @@ -3496,9 +4117,11 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: buttonSize, transition: context.transition ) - } else if incoming && !converted && !upgraded && canUpgrade { + } else if (incoming && !converted && !upgraded && canUpgrade) || canGiftUpgrade { let buttonTitle: String - if let upgradeStars, upgradeStars > 0 { + if canGiftUpgrade { + buttonTitle = strings.Gift_View_GiftUpgrade + } else if let upgradeStars, upgradeStars > 0 { buttonTitle = strings.Gift_View_UpgradeForFree } else { buttonTitle = strings.Gift_View_Upgrade @@ -3635,16 +4258,59 @@ private final class GiftViewSheetContent: CombinedComponent { isEnabled: true, displaysProgress: state.inProgress, action: { [weak state] in - state?.dismiss(animated: true) + if let state { + state.dismiss(animated: true) + } }), availableSize: buttonSize, transition: context.transition ) } let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: buttonChild.size) + + var buttonAlpha: CGFloat = 1.0 + if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift, !state.canSkip { + buttonAlpha = 0.0 + + let upgradeNextButton = upgradeNextButton.update( + component: PlainButtonComponent( + content: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "label", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_UpgradeNext, font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor))) + )), + AnyComponentWithIdentity(id: "icon", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: .starGift(gift: gift, price: ""), + mode: .buttonIcon + ) + )), + ], spacing: 5.0) + ), + action: { [weak state] in + state?.switchToNextUpgradable() + }, + animateScale: false + ), + environment: {}, + availableSize: buttonChild.size, + transition: .immediate + ) + context.add(upgradeNextButton + .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + } + context.add(buttonChild .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) .cornerRadius(10.0) + .opacity(buttonAlpha) ) originY += buttonChild.size.height originY += 7.0 @@ -3704,6 +4370,7 @@ final class GiftViewSheetComponent: CombinedComponent { backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, clipsContent: true, + hasDimView: false, externalState: sheetExternalState, animateOut: animateOut, onPan: { @@ -3714,6 +4381,7 @@ final class GiftViewSheetComponent: CombinedComponent { willDismiss: { if let controller = controller() as? GiftViewScreen { controller.dismissBalanceOverlay() + controller.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.3).withUserData(ViewControllerComponentContainer.AnimateOutTransition())) } } ), @@ -3727,6 +4395,7 @@ final class GiftViewSheetComponent: CombinedComponent { dismiss: { animated in if animated { if let controller = controller() as? GiftViewScreen { + controller.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.3).withUserData(ViewControllerComponentContainer.AnimateOutTransition())) controller.dismissAllTooltips() controller.dismissBalanceOverlay() animateOut.invoke(Action { _ in @@ -3788,20 +4457,22 @@ public class GiftViewScreen: ViewControllerComponentContainer { case upgradePreview([StarGift.UniqueGift.Attribute], String) case wearPreview(StarGift.UniqueGift) - var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?)? { + var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?, prepaidUpgradeHash: String?)? { switch self { case let .message(message): if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction { switch action.action { - case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, upgradeMessageId, peerId, senderId, savedId): + case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, _): var reference: StarGiftReference - if let peerId, let savedId { + if let peerId, let giftMessageId { + reference = .message(messageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: giftMessageId)) + } else if let peerId, let savedId { reference = .peer(peerId: peerId, id: savedId) } else { reference = .message(messageId: message.id) } - return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil) - case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, peerId, senderId, savedId, _, canTransferDate, canResaleDate): + return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil, prepaidUpgradeHash) + case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, _, peerId, senderId, savedId, _, canTransferDate, canResaleDate): var reference: StarGiftReference if let peerId, let savedId { reference = .peer(peerId: peerId, id: savedId) @@ -3825,13 +4496,13 @@ public class GiftViewScreen: ViewControllerComponentContainer { if case let .unique(uniqueGift) = gift { resellAmounts = uniqueGift.resellAmounts } - return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate) + return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate, nil) default: return nil } } case let .uniqueGift(gift, _), let .wearPreview(gift): - return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil) + return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil) case let .profileGift(peerId, gift): var messageId: EngineMessage.Id? if case let .message(messageIdValue) = gift.reference { @@ -3841,7 +4512,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { if case let .unique(uniqueGift) = gift.gift { resellAmounts = uniqueGift.resellAmounts } - return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate) + return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash) case .soldOutGift: return nil case .upgradePreview: @@ -3854,6 +4525,24 @@ public class GiftViewScreen: ViewControllerComponentContainer { private let context: AccountContext private let subject: GiftViewScreen.Subject + private var upgradableGiftsContext: ProfileGiftsContext? + fileprivate private(set) var upgradableGifts: [ProfileGiftsContext.State.StarGift]? + fileprivate var upgradedGiftReferences = Set() + private var upgradableDisposable: Disposable? + fileprivate var nextUpgradableGift: ProfileGiftsContext.State.StarGift? { + if let upgradableGifts = self.upgradableGifts { + return upgradableGifts.first(where: { gift in + if let reference = gift.reference { + if !self.upgradedGiftReferences.contains(reference) { + return true + } + } + return false + }) + } + return nil + } + fileprivate var showBalance = false { didSet { self.requestLayout(transition: .immediate) @@ -3864,13 +4553,14 @@ public class GiftViewScreen: ViewControllerComponentContainer { fileprivate let balanceOverlay = ComponentView() fileprivate let updateSavedToProfile: ((StarGiftReference, Bool) -> Void)? - fileprivate let convertToStars: (() -> Void)? + fileprivate let convertToStars: ((StarGiftReference) -> Void)? fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal)? - fileprivate let upgradeGift: ((Int64?, Bool) -> Signal)? + fileprivate let upgradeGift: ((Int64?, StarGiftReference, Bool) -> Signal)? fileprivate let buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal)? fileprivate let updateResellStars: ((CurrencyAmount?) -> Signal)? fileprivate let togglePinnedToTop: ((Bool) -> Bool)? fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)? + fileprivate let openChatTheme: (() -> Void)? public var disposed: () -> Void = {} @@ -3881,13 +4571,14 @@ public class GiftViewScreen: ViewControllerComponentContainer { index: Int? = nil, forceDark: Bool = false, updateSavedToProfile: ((StarGiftReference, Bool) -> Void)? = nil, - convertToStars: (() -> Void)? = nil, + convertToStars: ((StarGiftReference) -> Void)? = nil, transferGift: ((Bool, EnginePeer.Id) -> Signal)? = nil, - upgradeGift: ((Int64?, Bool) -> Signal)? = nil, + upgradeGift: ((Int64?, StarGiftReference, Bool) -> Signal)? = nil, buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal)? = nil, updateResellStars: ((CurrencyAmount?) -> Signal)? = nil, togglePinnedToTop: ((Bool) -> Bool)? = nil, - shareStory: ((StarGift.UniqueGift) -> Void)? = nil + shareStory: ((StarGift.UniqueGift) -> Void)? = nil, + openChatTheme: (() -> Void)? = nil ) { self.context = context self.subject = subject @@ -3900,6 +4591,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.updateResellStars = updateResellStars self.togglePinnedToTop = togglePinnedToTop self.shareStory = shareStory + self.openChatTheme = openChatTheme if case let .unique(gift) = subject.arguments?.gift, gift.resellForTonOnly { self.balanceCurrency = .ton @@ -3911,7 +4603,13 @@ public class GiftViewScreen: ViewControllerComponentContainer { if let allSubjects, !allSubjects.isEmpty { items.removeAll() for i in 0 ..< allSubjects.count { - items.append(GiftPagerComponent.Item(id: i, subject: allSubjects[i])) + var id: AnyHashable + if case let .profileGift(_, starGift) = allSubjects[i], let reference = starGift.reference { + id = reference.stringValue + } else { + id = i + } + items.append(GiftPagerComponent.Item(id: id, subject: allSubjects[i])) } } var dismissTooltipsImpl: (() -> Void)? @@ -3921,6 +4619,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { context: context, items: items, index: index ?? 0, + itemSpacing: 10.0, updated: { _, _ in dismissTooltipsImpl?() } @@ -3935,6 +4634,18 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.navigationPresentation = .flatModal self.automaticallyControlPresentationContextLayout = false + + if let gift = subject.arguments?.gift, case .generic = gift { + let upgradableGiftsContext = ProfileGiftsContext(account: context.account, peerId: context.account.peerId, collectionId: nil, sorting: .date, filter: [.displayed, .hidden, .limitedUpgradable], limit: 50) + self.upgradableDisposable = (upgradableGiftsContext.state + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let self else { + return + } + self.upgradableGifts = state.filteredGifts + }) + self.upgradableGiftsContext = upgradableGiftsContext + } } required public init(coder aDecoder: NSCoder) { @@ -3943,6 +4654,56 @@ public class GiftViewScreen: ViewControllerComponentContainer { deinit { self.disposed() + self.upgradableDisposable?.dispose() + } + + fileprivate func switchToNextUpgradable() { + guard let upgradableGifts = self.upgradableGifts else { + return + } + let peerId: EnginePeer.Id + if case let .profileGift(peerIdValue, _) = self.subject { + peerId = peerIdValue + } else { + peerId = self.context.account.peerId + } + var effectiveUpgradableGifts: [ProfileGiftsContext.State.StarGift] = [] + for gift in upgradableGifts { + if let reference = gift.reference { + if !self.upgradedGiftReferences.contains(reference) { + effectiveUpgradableGifts.append(gift) + } + } + } + + guard !effectiveUpgradableGifts.isEmpty else { + return + } + + var items: [GiftPagerComponent.Item] = [] + for i in 0 ..< effectiveUpgradableGifts.count { + let gift = effectiveUpgradableGifts[i] + var id: AnyHashable + if let reference = gift.reference { + id = reference.stringValue + } else { + id = i + } + items.append(GiftPagerComponent.Item(id: id, subject: .profileGift(peerId, gift))) + } + + self.updateComponent( + component: AnyComponent(GiftPagerComponent( + context: self.context, + items: items, + index: 0, + itemSpacing: 10.0, + updated: { [weak self] _, _ in + self?.dismissAllTooltips() + } + )), + transition: .spring(duration: 0.3) + ) } public override func viewDidLoad() { @@ -3950,7 +4711,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.view.disablesInteractiveModalDismiss = true - if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellAmounts { + if let arguments = self.subject.arguments, let resellAmounts = self.subject.arguments?.resellAmounts, !resellAmounts.isEmpty { if case let .unique(uniqueGift) = arguments.gift, case .peerId(self.context.account.peerId) = uniqueGift.owner { } else { self.showBalance = true @@ -4082,12 +4843,10 @@ public class GiftViewScreen: ViewControllerComponentContainer { } } -private func formatPercentage(_ value: Float) -> String { - return String(format: "%0.1f%%", value).replacingOccurrences(of: ".0%", with: "%").replacingOccurrences(of: ",0%", with: "%") +func formatPercentage(_ value: Float) -> String { + return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%" } - - private final class PeerCellComponent: Component { let context: AccountContext let theme: PresentationTheme @@ -4194,12 +4953,12 @@ private final class PeerCellComponent: Component { } } -private final class ButtonContentComponent: Component { +final class ButtonContentComponent: Component { let context: AccountContext let text: String let color: UIColor - public init( + init( context: AccountContext, text: String, color: UIColor @@ -4209,7 +4968,7 @@ private final class ButtonContentComponent: Component { self.color = color } - public static func ==(lhs: ButtonContentComponent, rhs: ButtonContentComponent) -> Bool { + static func ==(lhs: ButtonContentComponent, rhs: ButtonContentComponent) -> Bool { if lhs.context !== rhs.context { return false } @@ -4222,7 +4981,7 @@ private final class ButtonContentComponent: Component { return true } - public final class View: UIView { + final class View: UIView { private var component: ButtonContentComponent? private weak var componentState: EmptyComponentState? @@ -4280,11 +5039,11 @@ private final class ButtonContentComponent: Component { } } - public func makeView() -> View { + func makeView() -> View { return View(frame: CGRect()) } - public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } @@ -4694,3 +5453,16 @@ private struct GiftViewConfiguration { } } } + +private extension StarGiftReference { + var stringValue: String { + switch self { + case let .message(messageId): + return "m_\(messageId.id)" + case let .peer(peerId, id): + return "p_\(peerId.toInt64())_\(id)" + case let .slug(slug): + return "s_\(slug)" + } + } +} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/SlotsComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/SlotsComponent.swift new file mode 100644 index 0000000000..93d348a966 --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/SlotsComponent.swift @@ -0,0 +1,496 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import ImageBlur + +private let additionalInset: CGFloat = 4.0 +private let maskInset: CGFloat = 8.0 + +final class SlotsComponent: Component { + public typealias EnvironmentType = ChildEnvironment + + private let item: AnyComponent + private let items: [AnyComponentWithIdentity] + private let isAnimating: Bool + private let tintColor: UIColor? + private let verticalOffset: CGFloat + private let motionBlur: Bool + private let size: CGSize + + public init( + item: AnyComponent, + items: [AnyComponentWithIdentity], + isAnimating: Bool, + tintColor: UIColor? = nil, + verticalOffset: CGFloat = 0.0, + motionBlur: Bool = true, + size: CGSize + ) { + self.item = item + self.items = items + self.isAnimating = isAnimating + self.tintColor = tintColor + self.verticalOffset = verticalOffset + self.motionBlur = motionBlur + self.size = size + } + + public static func == (lhs: SlotsComponent, rhs: SlotsComponent) -> Bool { + if lhs.item != rhs.item { + return false + } + if lhs.items != rhs.items { + return false + } + if lhs.isAnimating != rhs.isAnimating { + return false + } + if lhs.tintColor != rhs.tintColor { + return false + } + if lhs.verticalOffset != rhs.verticalOffset { + return false + } + if lhs.motionBlur != rhs.motionBlur { + return false + } + if lhs.size != rhs.size { + return false + } + return true + } + + public final class View: UIView { + private var itemViews: [AnyHashable: ComponentView] = [:] + private var motionBlurLayers: [AnyHashable: SimpleLayer] = [:] + private var order: [AnyHashable] = [] + + private let containerView = UIView() + private let maskLayer = SimpleGradientLayer() + + private enum SpinState { + case idle + case spinning + case decelerating + case settled + } + + private var spinState: SpinState = .idle + private var isAnimating = false + private var animationLink: SharedDisplayLinkDriver.Link? + + private var currentIds = Set() + private var lastSpawnTime: Double? + private var currentInterval: Double = 0.09 + + private var motionBlurFactor: CGFloat = 1.0 + private var decelQueue: [AnyComponentWithIdentity] = [] + private var decelTotalSteps: Int = 0 + private var decelStepIndex: Int = 0 + + private let minSpawnInterval: Double = 0.10 + private let maxSpawnInterval: Double = 0.80 + + private let baseAnimDuration: Double = 0.18 + private let maxAnimDuration: Double = 0.5 + + private var component: SlotsComponent? + private var environment: Environment? + private var availableSize: CGSize? + + @inline(__always) private func lerp(_ a: Double, _ b: Double, _ t: Double) -> Double { a + (b - a) * t } + @inline(__always) private func clamp01(_ x: Double) -> Double { max(0.0, min(1.0, x)) } + + override init(frame: CGRect) { + super.init(frame: frame) + self.addSubview(self.containerView) + + self.containerView.clipsToBounds = true + + self.maskLayer.startPoint = CGPoint(x: 0.5, y: 0.0) + self.maskLayer.endPoint = CGPoint(x: 0.5, y: 1.0) + + let fade: CGFloat = 0.2 + self.maskLayer.locations = [0.0, NSNumber(value: Float(fade)), NSNumber(value: Float(1.0 - fade)), 1.0] + self.maskLayer.colors = [ + UIColor.black.withAlphaComponent(0.0).cgColor, + UIColor.black.withAlphaComponent(1.0).cgColor, + UIColor.black.withAlphaComponent(1.0).cgColor, + UIColor.black.withAlphaComponent(0.0).cgColor + ] + self.containerView.layer.mask = self.maskLayer + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func spawnRandomSlot(availableSize: CGSize) { + guard var items = self.component?.items, !items.isEmpty else { return } + items = items.filter { !self.currentIds.contains($0.id) } + guard let randomItem = items.randomElement() else { return } + + self.spawnSlot(item: randomItem, availableSize: availableSize) {} + } + + private func spawnSlot( + item: AnyComponentWithIdentity, + isFinal: Bool = false, + availableSize: CGSize, + animDuration: Double? = nil, + completion: @escaping () -> Void + ) { + guard let component = self.component, let environment = self.environment else { return } + + self.currentIds.insert(item.id) + self.lastSpawnTime = CACurrentMediaTime() + + let itemView = self.itemViews[item.id] ?? ComponentView() + self.itemViews[item.id] = itemView + + let size = itemView.update( + transition: .immediate, + component: item.component, + environment: { environment[ChildEnvironment.self] }, + containerSize: availableSize + ) + if let view = itemView.view { + if view.superview == nil { + if let tintColor = component.tintColor { + view.layer.layerTintColor = tintColor.cgColor + } + view.layer.allowsGroupOpacity = true + self.containerView.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: 0.0, y: -size.height - additionalInset), size: size) + + let travelDistance = (size.height + maskInset + additionalInset) * 2.0 + let pitch = (size.height + additionalInset) + + if isFinal { + var finalFrame = view.frame + finalFrame.origin.y = maskInset + view.frame = finalFrame + + let fromY = size.height + maskInset + additionalInset + let overshoot: CGFloat = 7.0 + + let anim = CAKeyframeAnimation(keyPath: "position.y") + anim.isAdditive = true + anim.values = [ fromY, -overshoot, 0.0 ] + anim.keyTimes = [0.0, 0.7, 1.0] + anim.timingFunctions = [ + CAMediaTimingFunction(name: .easeOut), + CAMediaTimingFunction(name: .easeInEaseOut) + ] + anim.duration = 0.5 + + CATransaction.begin() + CATransaction.setCompletionBlock { + completion() + self.currentIds.remove(item.id) + self.finishSettled() + } + view.layer.add(anim, forKey: "finalOvershoot") + CATransaction.commit() + } + else { + let duration: Double = animDuration ?? baseAnimDuration + + view.layer.animatePosition( + from: CGPoint(x: 0.0, y: (size.height + maskInset + additionalInset) * 2.0), + to: .zero, + duration: duration, + timingFunction: CAMediaTimingFunctionName.linear.rawValue, + additive: true, + completion: { _ in + completion() + self.currentIds.remove(item.id) + } + ) + + let intervalForConstantSpacing = Double(pitch / travelDistance) * duration + self.currentInterval = intervalForConstantSpacing + } + } + + self.setMotionBlurFactor(id: item.id, factor: self.motionBlurFactor, transition: .immediate) + } + + private func setMotionBlurFactor(id: AnyHashable, factor: CGFloat, transition: ComponentTransition) { + guard let component = self.component, component.motionBlur, let itemView = self.itemViews[id]?.view else { + return + } + + if factor != 0.0 { + let motionBlurLayer: SimpleLayer + if let current = self.motionBlurLayers[id] { + motionBlurLayer = current + } else { + motionBlurLayer = SimpleLayer() + + let image = generateImage(itemView.bounds.size, rotatedContext: { size, context in + UIGraphicsPushContext(context) + defer { UIGraphicsPopContext() } + context.clear(CGRect(origin: CGPoint(), size: size)) + itemView.layer.render(in: context) + }) + if let image { + motionBlurLayer.contents = verticalBlurredImage(image, radius: 8.0)?.cgImage + } + motionBlurLayer.contentsScale = itemView.layer.contentsScale + self.motionBlurLayers[id] = motionBlurLayer + itemView.layer.addSublayer(motionBlurLayer) + + motionBlurLayer.position = CGPoint(x: itemView.bounds.size.width * 0.5, y: itemView.bounds.size.height * 0.5) + motionBlurLayer.bounds = CGRect(origin: CGPoint(), size: itemView.bounds.size) + + if let tintColor = component.tintColor { + motionBlurLayer.layerTintColor = tintColor.cgColor + } + } + + let scaleFactor = 1.0 * (1.0 - factor) + 1.25 * factor + let opacityFactor = 1.0 * (1.0 - factor) + 0.6 * factor + transition.setTransform(layer: motionBlurLayer, transform: CATransform3DMakeScale(1.0, scaleFactor, 1.0)) + transition.setAlpha(layer: itemView.layer, alpha: opacityFactor) + } else if let motionBlurLayer = self.motionBlurLayers[id] { + self.motionBlurLayers.removeValue(forKey: id) + transition.setAlpha(layer: motionBlurLayer, alpha: 0.0, completion: { [weak motionBlurLayer] _ in + motionBlurLayer?.removeFromSuperlayer() + }) + transition.setTransform(layer: motionBlurLayer, transform: CATransform3DIdentity) + } + } + + private func beginSpinning() { + self.spinState = .spinning + self.isAnimating = true + self.motionBlurFactor = 1.0 + self.currentInterval = 0.1 + + self.ensureDisplayLink() + } + + private func beginDeceleration() { + guard let component = self.component, self.spinState == .spinning || self.spinState == .decelerating else { return } + + self.spinState = .decelerating + self.isAnimating = false + + var queue: [AnyComponentWithIdentity] = [] + if !component.items.isEmpty { + let shuffled = Array(component.items.shuffled().prefix(3)) + queue.append(contentsOf: shuffled) + } + queue.append(AnyComponentWithIdentity(id: "final", component: component.item)) + + self.decelQueue = queue + self.decelTotalSteps = queue.count + self.decelStepIndex = 0 + + self.motionBlurFactor = max(self.motionBlurFactor, 0.0001) + + self.ensureDisplayLink() + } + + private func ensureDisplayLink() { + guard self.animationLink == nil else { + return + } + self.animationLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] _ in + self?.tick() + }) + } + + private func invalidateDisplayLinkIfIdle() { + if self.spinState == .idle || self.spinState == .settled { + self.animationLink?.invalidate() + self.animationLink = nil + } + } + + private func applyEdge3DSquish() { + let H = self.containerView.bounds.height + guard H > 0 else { return } + + CATransaction.begin() + CATransaction.setDisableActions(true) + for (_, cv) in self.itemViews { + guard let v = cv.view, v.superview === self.containerView else { continue } + let midY = liveMidY(in: self.containerView, of: v) + let scaleY = edgeScaleY(for: midY, containerHeight: H) + var t = CATransform3DIdentity + t = CATransform3DScale(t, 1.0, scaleY, 1.0) + v.layer.transform = t + } + CATransaction.commit() + } + + private func tick() { + guard let availableSize = self.availableSize else { + return + } + let now = CACurrentMediaTime() + + switch self.spinState { + case .spinning: + if let last = self.lastSpawnTime, now - last >= self.currentInterval || self.lastSpawnTime == nil { + self.spawnRandomSlot(availableSize: availableSize) + } + case .decelerating: + let t = clamp01(self.decelTotalSteps > 1 ? Double(self.decelStepIndex) / Double(self.decelTotalSteps - 1) : 1.0) + + if let last = self.lastSpawnTime, now - last >= self.currentInterval { + if !self.decelQueue.isEmpty { + let next = self.decelQueue.removeFirst() + let isFinal = self.decelQueue.isEmpty + let animDuration = isFinal ? nil : lerp(baseAnimDuration, maxAnimDuration, t) + self.spawnSlot(item: next, isFinal: isFinal, availableSize: availableSize, animDuration: animDuration, completion: {}) + + self.motionBlurFactor = CGFloat(1.0 - t) + + self.decelStepIndex += 1 + } + } else if self.lastSpawnTime == nil { + if !self.decelQueue.isEmpty { + let next = self.decelQueue.removeFirst() + self.spawnSlot(item: next, availableSize: availableSize) {} + } + } + + case .settled, .idle: + self.invalidateDisplayLinkIfIdle() + } + + self.applyEdge3DSquish() + } + + private func finishSettled() { + for (id, _) in self.motionBlurLayers { + self.setMotionBlurFactor(id: id, factor: 0.0, transition: .easeInOut(duration: 0.2)) + } + self.motionBlurLayers.removeAll() + + self.spinState = .settled + self.decelQueue.removeAll() + self.invalidateDisplayLinkIfIdle() + } + + func update( + component: SlotsComponent, + availableSize: CGSize, + state: EmptyComponentState, + environment: Environment, + transition: ComponentTransition + ) -> CGSize { + self.component = component + self.environment = environment + self.availableSize = availableSize + + let size = component.size + self.containerView.frame = CGRect(origin: CGPoint(x: 0.0, y: component.verticalOffset), size: size).insetBy(dx: 0.0, dy: -maskInset) + self.maskLayer.frame = CGRect(origin: .zero, size: self.containerView.bounds.size) + + let wasAnimating = self.isAnimating + let nowAnimating = component.isAnimating + + if nowAnimating && !wasAnimating { + self.beginSpinning() + self.spawnRandomSlot(availableSize: availableSize) + } else if !nowAnimating && wasAnimating { + self.beginDeceleration() + } else if nowAnimating && self.spinState == .settled { + self.beginSpinning() + self.spawnRandomSlot(availableSize: availableSize) + } + + if let tintColor = component.tintColor { + for (id, itemView) in self.itemViews { + if let itemLayer = itemView.view?.layer { + transition.setTintColor(layer: itemLayer, color: tintColor) + } + if let blurLayer = self.motionBlurLayers[id] { + transition.setTintColor(layer: blurLayer, color: tintColor) + } + } + } + + return size + } + } + + public func makeView() -> View { + return View() + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private let minScaleYAtEdge: CGFloat = 0.7 +private let squishFalloff: CGFloat = 0.12 + +private func smoothstep(_ x: CGFloat) -> CGFloat { + let t = max(0.0, min(1.0, x)) + return t * t * (3.0 - 2.0 * t) +} + +private func liveMidY(in container: UIView, of view: UIView) -> CGFloat { + if let pres = view.layer.presentation() { + let p = container.layer.convert(pres.position, from: view.layer.superlayer) + return p.y + } + return view.center.y +} + +private func edgeScaleY(for midY: CGFloat, containerHeight H: CGFloat) -> CGFloat { + guard H > 0 else { return 1.0 } + let d = abs((midY - H * 0.5) / (H * 0.5)) + let uRaw = (d - squishFalloff) / (1.0 - squishFalloff) + let u = smoothstep(max(0.0, min(1.0, uRaw))) + return (1.0 - u) + minScaleYAtEdge * u +} + + +final class SpacerComponent: Component { + let size: CGSize + + init( + size: CGSize + ) { + self.size = size + } + + static func ==(lhs: SpacerComponent, rhs: SpacerComponent) -> Bool { + return lhs.size == rhs.size + } + + final class View: UIView { + private var component: SpacerComponent? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: SpacerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + return component.size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift index e11c583606..39897acf28 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift @@ -1018,6 +1018,30 @@ public final class InteractiveTextNodeLayout: NSObject { } return nil } + + public func sizeForGlyphCount(glyphCount: Int) -> CGSize { + var height: CGFloat = 0.0 + if !self.segments.isEmpty, let line = self.segments[0].lines.first { + height = line.frame.maxY + } + var count = 0 + for segment in self.segments { + for line in segment.lines { + if count >= glyphCount { + break + } + let glyphRuns = CTLineGetGlyphRuns(line.line) as NSArray + for run in glyphRuns { + let run = run as! CTRun + let glyphCount = CTRunGetGlyphCount(run) + count += Int(glyphCount) + } + height = max(height, line.frame.maxY) + } + } + height += self.insets.top + self.insets.bottom + 2.0 + return CGSize(width: self.size.width, height: ceil(height)) + } } private func addSpoiler(line: InteractiveTextNodeLine, ascent: CGFloat, descent: CGFloat, startIndex: Int, endIndex: Int) { @@ -1144,6 +1168,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn } public internal(set) var cachedLayout: InteractiveTextNodeLayout? + public internal(set) var revealGlyphCount: Int? public var renderContentTypes: RenderContentTypes = .all private var contentItemLayers: [Int: TextContentItemLayer] = [:] @@ -2068,6 +2093,65 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn }) } } + + public func getGlyphCount() -> Int { + guard let cachedLayout = self.cachedLayout else { + return 0 + } + + var count: Int = 0 + + for segment in cachedLayout.segments { + for line in segment.lines { + let glyphRuns = CTLineGetGlyphRuns(line.line) as NSArray + for run in glyphRuns { + let run = run as! CTRun + let glyphCount = CTRunGetGlyphCount(run) + count += Int(glyphCount) + } + } + } + + return count + } + + public func updateRevealGlyphCount(count: Int?) { + guard let cachedLayout = self.cachedLayout else { + return + } + + self.revealGlyphCount = count + + if let count { + var nextItemId = 0 + var currentCount = 0 + for segment in cachedLayout.segments { + let itemId = nextItemId + nextItemId += 1 + + var segmentGlyphCount = 0 + for line in segment.lines { + let glyphRuns = CTLineGetGlyphRuns(line.line) as NSArray + for run in glyphRuns { + let run = run as! CTRun + let glyphCount = CTRunGetGlyphCount(run) + segmentGlyphCount += Int(glyphCount) + } + } + + let segmentInnerCount = min(max(0, count - currentCount), segmentGlyphCount) + if let item = self.contentItemLayers[itemId] { + item.updateMaxGlyphDrawCount(value: segmentInnerCount) + } + + currentCount += segmentGlyphCount + } + } else { + for (_, item) in self.contentItemLayers { + item.updateMaxGlyphDrawCount(value: nil) + } + } + } } final class TextContentItem { @@ -2148,11 +2232,13 @@ final class TextContentItemLayer: SimpleLayer { let size: CGSize let item: TextContentItem let mask: RenderMask? + let maxGlyphDrawCount: Int? - init(size: CGSize, item: TextContentItem, mask: RenderMask?) { + init(size: CGSize, item: TextContentItem, mask: RenderMask?, maxGlyphDrawCount: Int?) { self.size = size self.item = item self.mask = mask + self.maxGlyphDrawCount = maxGlyphDrawCount super.init() } @@ -2232,6 +2318,8 @@ final class TextContentItemLayer: SimpleLayer { let offset = params.item.contentOffset let alignment: NSTextAlignment = .left + var drawnGlyphCount = 0 + for i in 0 ..< params.item.segment.lines.count { let line = params.item.segment.lines[i] @@ -2261,6 +2349,15 @@ final class TextContentItemLayer: SimpleLayer { for run in glyphRuns { let run = run as! CTRun let glyphCount = CTRunGetGlyphCount(run) + + var runDrawGlyphCount = glyphCount + if let maxGlyphDrawCount = params.maxGlyphDrawCount { + if drawnGlyphCount >= maxGlyphDrawCount { + break + } + runDrawGlyphCount = CFIndex(max(0, min(Int(glyphCount), maxGlyphDrawCount - drawnGlyphCount))) + } + let attributes = CTRunGetAttributes(run) as NSDictionary if attributes["Attribute__EmbeddedItem"] != nil { continue @@ -2310,12 +2407,14 @@ final class TextContentItemLayer: SimpleLayer { let stringRange = CTRunGetStringRange(run) if line.attachments.contains(where: { $0.range.contains(stringRange.location) }) { } else { - CTRunDraw(run, context, CFRangeMake(0, glyphCount)) + CTRunDraw(run, context, CFRangeMake(0, runDrawGlyphCount)) } } else { - CTRunDraw(run, context, CFRangeMake(0, glyphCount)) + CTRunDraw(run, context, CFRangeMake(0, runDrawGlyphCount)) } + drawnGlyphCount += Int(glyphCount) + if fixDoubleEmoji { context.setBlendMode(.normal) } @@ -2464,6 +2563,8 @@ final class TextContentItemLayer: SimpleLayer { private var isAnimating: Bool = false private var currentContentMask: RenderMask? + private var maxGlyphDrawCount: Int? + init(displaysAsynchronously: Bool) { self.renderNode = RenderNode() self.renderNode.displaysAsynchronously = displaysAsynchronously @@ -2483,6 +2584,17 @@ final class TextContentItemLayer: SimpleLayer { fatalError("init(coder:) has not been implemented") } + func updateMaxGlyphDrawCount(value: Int?) { + if self.maxGlyphDrawCount != value { + self.maxGlyphDrawCount = value + + if let renderParams = self.renderNode.params { + self.renderNode.params = RenderParams(size: renderParams.size, item: renderParams.item, mask: renderParams.mask, maxGlyphDrawCount: self.maxGlyphDrawCount) + self.renderNode.displayImmediately() + } + } + } + func update( params: Params, animation: ListViewItemUpdateAnimation, @@ -2735,7 +2847,7 @@ final class TextContentItemLayer: SimpleLayer { self.currentContentMask = contentMask - self.renderNode.params = RenderParams(size: contentFrame.size, item: params.item, mask: staticContentMask) + self.renderNode.params = RenderParams(size: contentFrame.size, item: params.item, mask: staticContentMask, maxGlyphDrawCount: self.maxGlyphDrawCount) if synchronously { if let spoilerExpandRect, animation.isAnimated { let localSpoilerExpandRect = spoilerExpandRect.offsetBy(dx: -self.renderNode.frame.minX, dy: -self.renderNode.frame.minY) diff --git a/submodules/TelegramUI/Components/MarqueeComponent/Sources/MarqueeComponent.swift b/submodules/TelegramUI/Components/MarqueeComponent/Sources/MarqueeComponent.swift index 29f0d81dcc..d26866caad 100644 --- a/submodules/TelegramUI/Components/MarqueeComponent/Sources/MarqueeComponent.swift +++ b/submodules/TelegramUI/Components/MarqueeComponent/Sources/MarqueeComponent.swift @@ -9,6 +9,27 @@ private let animationDelay: TimeInterval = 2.5 private let spacing: CGFloat = 20.0 public final class MarqueeComponent: Component { + let attributedText: NSAttributedString + let maxWidth: CGFloat? + + public init( + attributedText: NSAttributedString, + maxWidth: CGFloat? = nil + ) { + self.attributedText = attributedText + self.maxWidth = maxWidth + } + + public static func ==(lhs: MarqueeComponent, rhs: MarqueeComponent) -> Bool { + if lhs.attributedText != rhs.attributedText { + return false + } + if lhs.maxWidth != rhs.maxWidth { + return false + } + return true + } + public static let innerPadding: CGFloat = 16.0 private final class MeasureState: Equatable { @@ -41,7 +62,9 @@ public final class MarqueeComponent: Component { private let containerLayer = SimpleLayer() private let textLayer = SimpleLayer() private let duplicateTextLayer = SimpleLayer() + private let maskContainerLayer = SimpleLayer() private let gradientMaskLayer = SimpleGradientLayer() + private let solidEdgeMaskLayer = SimpleLayer() private var isAnimating = false private var isOverflowing = false @@ -56,6 +79,9 @@ public final class MarqueeComponent: Component { self.containerLayer.addSublayer(self.textLayer) self.containerLayer.addSublayer(self.duplicateTextLayer) + + self.maskContainerLayer.addSublayer(self.gradientMaskLayer) + self.maskContainerLayer.addSublayer(self.solidEdgeMaskLayer) } required init?(coder: NSCoder) { @@ -66,6 +92,11 @@ public final class MarqueeComponent: Component { let previousComponent = self.component self.component = component + var availableSize = availableSize + if let maxWidth = component.maxWidth { + availableSize.width = maxWidth + } + let attributedText = component.attributedText if let measureState = self.measureState { if measureState.attributedText.isEqual(to: attributedText) && measureState.availableSize == availableSize { @@ -97,27 +128,34 @@ public final class MarqueeComponent: Component { self.startAnimation(force: previousComponent?.attributedText != attributedText) } else { self.stopAnimation() - self.textLayer.frame = CGRect(origin: CGPoint(x: innerPadding, y: 0.0), size: boundingRect.size) + self.textLayer.frame = CGRect(origin: .zero, size: boundingRect.size) self.textLayer.contents = image.cgImage self.duplicateTextLayer.frame = .zero self.duplicateTextLayer.contents = nil self.layer.mask = nil } - return CGSize(width: min(measureState.size.width + innerPadding * 2.0, availableSize.width), height: measureState.size.height) + return CGSize(width: min(measureState.size.width, availableSize.width), height: measureState.size.height) } private func setupMarqueeTextLayers(textImage: CGImage, textWidth: CGFloat, containerWidth: CGFloat) { - self.textLayer.frame = CGRect(x: innerPadding, y: 0, width: textWidth, height: self.containerLayer.bounds.height) + self.textLayer.frame = CGRect(x: 0.0, y: 0, width: textWidth, height: self.containerLayer.bounds.height) self.textLayer.contents = textImage - self.duplicateTextLayer.frame = CGRect(x: innerPadding + textWidth + spacing, y: 0, width: textWidth, height: self.containerLayer.bounds.height) + self.duplicateTextLayer.frame = CGRect(x: textWidth + spacing, y: 0, width: textWidth, height: self.containerLayer.bounds.height) self.duplicateTextLayer.contents = textImage self.containerLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: textWidth * 2.0 + spacing, height: self.containerLayer.bounds.height)) } private func setupGradientMask(size: CGSize) { + let edgePercentage = innerPadding / size.width + + self.maskContainerLayer.frame = CGRect(origin: .zero, size: size) + + self.solidEdgeMaskLayer.frame = CGRect(origin: .zero, size: CGSize(width: innerPadding, height: size.height)) + self.solidEdgeMaskLayer.backgroundColor = UIColor.black.cgColor + self.gradientMaskLayer.frame = CGRect(origin: .zero, size: size) self.gradientMaskLayer.colors = [ UIColor.clear.cgColor, @@ -125,22 +163,20 @@ public final class MarqueeComponent: Component { UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor, - UIColor.clear.cgColor + UIColor.clear.cgColor, ] self.gradientMaskLayer.startPoint = CGPoint(x: 0.0, y: 0.5) self.gradientMaskLayer.endPoint = CGPoint(x: 1.0, y: 0.5) - - let edgePercentage = innerPadding / size.width self.gradientMaskLayer.locations = [ 0.0, - NSNumber(value: edgePercentage * 0.4), + NSNumber(value: edgePercentage * 0.1), NSNumber(value: edgePercentage), NSNumber(value: 1.0 - edgePercentage), - NSNumber(value: 1.0 - edgePercentage * 0.4), + NSNumber(value: 1.0 - edgePercentage * 0.1), 1.0 ] - - self.layer.mask = self.gradientMaskLayer + + self.layer.mask = self.maskContainerLayer } private func startAnimation(force: Bool = false) { @@ -157,6 +193,14 @@ public final class MarqueeComponent: Component { guard self.isAnimating else { return } + let values: [NSNumber] = [1.0, 0.0, 0.0, 1.0] + let keyTimes: [NSNumber] = [0.0, 0.02, 0.98, 1.0] + self.solidEdgeMaskLayer.animateKeyframes( + values: values, + keyTimes: keyTimes, + duration: duration, + keyPath: "opacity" + ) self.containerLayer.animateBoundsOriginXAdditive(from: 0.0, to: distance, duration: duration, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, completion: { finished in if finished { self.isAnimating = false @@ -171,20 +215,7 @@ public final class MarqueeComponent: Component { self.isAnimating = false } } - - public let attributedText: NSAttributedString - - public init(attributedText: NSAttributedString) { - self.attributedText = attributedText - } - - public static func ==(lhs: MarqueeComponent, rhs: MarqueeComponent) -> Bool { - if lhs.attributedText != rhs.attributedText { - return false - } - return true - } - + public func makeView() -> View { return View() } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift index dd77fbaf44..2c63df0f7a 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorUtils.swift @@ -98,6 +98,7 @@ func loadTexture(image: UIImage, device: MTLDevice) -> MTLTexture? { let data = dataForImage(image) texture?.replace(region: region, mipmapLevel: 0, withBytes: data, bytesPerRow: bytesPerRow) + data.deallocate() return texture } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 58defa6352..de9497d768 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3730,7 +3730,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } } else if case let .gift(gift) = subject { isGift = true - let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil))] + let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, isPrepaidUpgrade: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil))] let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: Namespaces.Message.Cloud, id: -1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages = .single([message]) } else { diff --git a/submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/BUILD b/submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/BUILD new file mode 100644 index 0000000000..25de730ac0 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/BUILD @@ -0,0 +1,25 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerMessagesMediaPlaylist", + module_name = "PeerMessagesMediaPlaylist", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/TelegramUIPreferences", + "//submodules/AccountContext", + "//submodules/MusicAlbumArtResources", + "//submodules/TextFormat", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/Sources/PeerMessagesMediaPlaylist.swift similarity index 90% rename from submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift rename to submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/Sources/PeerMessagesMediaPlaylist.swift index 9eb5ba1858..6565bf17c5 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist/Sources/PeerMessagesMediaPlaylist.swift @@ -47,22 +47,29 @@ private func extractFileMedia(_ message: Message) -> TelegramMediaFile? { return file } -final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { - let id: SharedMediaPlaylistItemId - let message: Message +public final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { + public let id: SharedMediaPlaylistItemId + public let message: Message + public let isSavedMusic: Bool - init(message: Message) { + public init(message: Message, isSavedMusic: Bool) { self.id = PeerMessagesMediaPlaylistItemId(messageId: message.id, messageIndex: message.index) self.message = message + self.isSavedMusic = isSavedMusic } - var stableId: AnyHashable { + public var stableId: AnyHashable { return MessageMediaPlaylistItemStableId(stableId: message.stableId) } - lazy var playbackData: SharedMediaPlaybackData? = { + public lazy var playbackData: SharedMediaPlaybackData? = { if let file = extractFileMedia(self.message) { - let fileReference = FileMediaReference.message(message: MessageReference(self.message), media: file) + let fileReference: FileMediaReference + if self.isSavedMusic, let peer = self.message.peers[self.message.id.peerId], let peerReference = PeerReference(peer) { + fileReference = .savedMusic(peer: peerReference, media: file) + } else { + fileReference = .message(message: MessageReference(self.message), media: file) + } let source = SharedMediaPlaybackDataSource.telegramFile(reference: fileReference, isCopyProtected: self.message.isCopyProtected(), isViewOnce: self.message.minAutoremoveOrClearTimeout == viewOnceTimeout) for attribute in file.attributes { switch attribute { @@ -95,7 +102,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { return nil }() - lazy var displayData: SharedMediaPlaybackDisplayData? = { + public lazy var displayData: SharedMediaPlaybackDisplayData? = { if let file = extractFileMedia(self.message) { let text = self.message.text var entities: [MessageTextEntity] = [] @@ -385,16 +392,16 @@ private struct PlaybackStack { } } -final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { - let context: AccountContext +public final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { + public let context: AccountContext private let messagesLocation: PeerMessagesPlaylistLocation private let chatLocationContextHolder: Atomic? - var location: SharedMediaPlaylistLocation { + public var location: SharedMediaPlaylistLocation { return self.messagesLocation } - var currentItemDisappeared: (() -> Void)? + public var currentItemDisappeared: (() -> Void)? private let navigationDisposable = MetaDisposable() private let loadMoreDisposable = MetaDisposable() @@ -408,16 +415,16 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { private var loadingMore: Bool = false private var playedToEnd: Bool = false private var order: MusicPlaybackSettingsOrder = .regular - private(set) var looping: MusicPlaybackSettingsLooping = .none + public private(set) var looping: MusicPlaybackSettingsLooping = .none - let id: SharedMediaPlaylistId + public let id: SharedMediaPlaylistId private let stateValue = Promise() - var state: Signal { + public var state: Signal { return self.stateValue.get() } - init(context: AccountContext, location: PeerMessagesPlaylistLocation, chatLocationContextHolder: Atomic?) { + public init(context: AccountContext, location: PeerMessagesPlaylistLocation, chatLocationContextHolder: Atomic?) { assert(Queue.mainQueue().isCurrent()) self.id = location.playlistId @@ -426,13 +433,15 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.chatLocationContextHolder = chatLocationContextHolder self.messagesLocation = location - switch self.messagesLocation { - case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, messageId, _): + switch self.messagesLocation.effectiveLocation(context: context) { + case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, _, messageId, _): self.loadItem(anchor: .messageId(messageId), navigation: .later, reversed: self.order == .reversed) case let .recentActions(message): self.loadingItem = false self.currentItem = (message, []) self.updateState() + case .savedMusic: + break } } @@ -442,7 +451,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.currentlyObservedMessageDisposable.dispose() } - func control(_ action: SharedMediaPlaylistControlAction) { + public func control(_ action: SharedMediaPlaylistControlAction) { assert(Queue.mainQueue().isCurrent()) switch action { @@ -488,7 +497,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } } - func setOrder(_ order: MusicPlaybackSettingsOrder) { + public func setOrder(_ order: MusicPlaybackSettingsOrder) { if self.order != order { self.order = order self.playbackStack.clear() @@ -499,7 +508,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } } - func setLooping(_ looping: MusicPlaybackSettingsLooping) { + public func setLooping(_ looping: MusicPlaybackSettingsLooping) { if self.looping != looping { self.looping = looping self.updateState() @@ -507,21 +516,27 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } private func updateState() { + var isSavedMusic = false + if case .savedMusic = self.messagesLocation { + isSavedMusic = true + } + var item: MessageMediaPlaylistItem? var nextItem: MessageMediaPlaylistItem? var previousItem: MessageMediaPlaylistItem? if let (message, aroundMessages) = self.currentItem { - item = MessageMediaPlaylistItem(message: message) + item = MessageMediaPlaylistItem(message: message, isSavedMusic: isSavedMusic) for around in aroundMessages { if around.index < message.index { - previousItem = MessageMediaPlaylistItem(message: around) + previousItem = MessageMediaPlaylistItem(message: around, isSavedMusic: isSavedMusic) } else { - nextItem = MessageMediaPlaylistItem(message: around) + nextItem = MessageMediaPlaylistItem(message: around, isSavedMusic: isSavedMusic) } } } self.stateValue.set(.single(SharedMediaPlaylistState(loading: self.loadingItem, playedToEnd: self.playedToEnd, item: item, nextItem: nextItem, previousItem: previousItem, order: self.order, looping: self.looping))) - if item?.message.id != self.currentlyObservedMessageId { + if case .custom = self.messagesLocation.effectiveLocation(context: self.context) { + } else if item?.message.id != self.currentlyObservedMessageId { self.currentlyObservedMessageId = item?.message.id if let id = item?.message.id { self.currentlyObservedMessageDisposable.set((self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: id)) @@ -554,10 +569,10 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } else { namespaces = .not(Namespaces.Message.allNonRegular) } - + switch anchor { case let .messageId(messageId): - switch self.messagesLocation { + switch self.messagesLocation.effectiveLocation(context: self.context) { case let .messages(chatLocation, tagMask, _): let historySignal = self.context.account.postbox.messageAtId(messageId) |> take(1) @@ -593,29 +608,30 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { strongSelf.updateState() } })) - case let .custom(messages, at, _): + case let .custom(messages, _, at, _): self.navigationDisposable.set((messages |> take(1) |> deliverOnMainQueue).startStrict(next: { [weak self] messages in - if let strongSelf = self { - assert(strongSelf.loadingItem) - - strongSelf.loadingItem = false - if let message = messages.0.first(where: { $0.id == at }) { - strongSelf.playbackStack.clear() - strongSelf.playbackStack.push(message.id) - if let (message, aroundMessages, _) = navigatedMessageFromMessages(messages.0, anchorIndex: message.index, position: .exact) { - strongSelf.currentItem = (message, aroundMessages) - } else { - strongSelf.currentItem = (message, []) - } - strongSelf.playedToEnd = false - } else { - strongSelf.currentItem = nil - strongSelf.playedToEnd = true - } - strongSelf.updateState() + guard let self else { + return } + assert(self.loadingItem) + + self.loadingItem = false + if let message = messages.0.first(where: { $0.id == at }) { + self.playbackStack.clear() + self.playbackStack.push(message.id) + if let (message, aroundMessages, _) = navigatedMessageFromMessages(messages.0, anchorIndex: message.index, position: .exact) { + self.currentItem = (message, aroundMessages) + } else { + self.currentItem = (message, []) + } + self.playedToEnd = false + } else { + self.currentItem = nil + self.playedToEnd = true + } + self.updateState() })) default: self.navigationDisposable.set((self.context.account.postbox.messageAtId(messageId) @@ -637,7 +653,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { })) } case let .index(index): - switch self.messagesLocation { + switch self.messagesLocation.effectiveLocation(context: self.context) { case let .messages(chatLocation, tagMask, _): var inputIndex: Signal? let looping = self.looping @@ -769,7 +785,9 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.loadingItem = false self.currentItem = (message, []) self.updateState() - case let .custom(messages, _, loadMore): + case .savedMusic: + fatalError() + case let .custom(messages, _, _, loadMore): let inputIndex: Signal let looping = self.looping switch self.order { @@ -822,6 +840,16 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } if case .all = looping { + if let first = messages.first { + if let (message, aroundMessages, _) = navigatedMessageFromMessages(messages, anchorIndex: first.index, position: .exact) { + switch navigation { + case .random: + return .single(((message, []), messages.count, false)) + default: + return .single(((message, aroundMessages), messages.count, false)) + } + } + } return .single((nil, messages.count, false)) } else { if hasMore { @@ -879,7 +907,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } } - func onItemPlaybackStarted(_ item: SharedMediaPlaylistItem) { + public func onItemPlaybackStarted(_ item: SharedMediaPlaylistItem) { if let item = item as? MessageMediaPlaylistItem { switch self.messagesLocation { case .recentActions: diff --git a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift index 8b20e10e96..9ef9bbcd1b 100644 --- a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift +++ b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift @@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio } } existingThreadIds.insert(value.threadId) - entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) + entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) index += 1 } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift index 24b3b1e1ac..dccc9e198b 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift @@ -278,8 +278,38 @@ public final class PeerInfoCoverComponent: Component { } } - public func animateTransition() { + public func animateSwipeTransition() { if let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() { + let backgroundSnapshotLayer = SimpleLayer() + backgroundSnapshotLayer.masksToBounds = true + backgroundSnapshotLayer.allowsGroupOpacity = true + backgroundSnapshotLayer.backgroundColor = self.backgroundView.backgroundColor?.cgColor + backgroundSnapshotLayer.frame = self.backgroundView.frame + self.layer.insertSublayer(backgroundSnapshotLayer, above: self.backgroundGradientLayer) + + gradientSnapshotLayer.frame = self.backgroundGradientLayer.convert(self.backgroundGradientLayer.bounds, to: self.backgroundView.layer) + backgroundSnapshotLayer.addSublayer(gradientSnapshotLayer) + + let mask = CAGradientLayer() + mask.startPoint = CGPoint(x: 0.0, y: 0.5) + mask.endPoint = CGPoint(x: 1.0, y: 0.5) + mask.frame = CGRect(origin: CGPoint(x: backgroundSnapshotLayer.bounds.width, y: 0.0), size: CGSize(width: backgroundSnapshotLayer.bounds.width * 2.0, height: backgroundSnapshotLayer.bounds.height)) + mask.colors = [ + UIColor.white.withAlphaComponent(0.0).cgColor, + UIColor.white.cgColor, + UIColor.white.cgColor, + ] + mask.locations = [0.0, 0.5, 1.0] + backgroundSnapshotLayer.mask = mask + + mask.animatePosition(from: CGPoint(x: -backgroundSnapshotLayer.bounds.width * 2.0, y: 0.0), to: .zero, duration: 0.35, timingFunction: CAMediaTimingFunctionName.linear.rawValue, additive: true, completion: { [weak backgroundSnapshotLayer] _ in + backgroundSnapshotLayer?.removeFromSuperlayer() + }) + } + } + + public func animateTransition(background: Bool = true, bounce: Bool = true) { + if background, let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() { let backgroundSnapshotLayer = SimpleLayer() backgroundSnapshotLayer.allowsGroupOpacity = true backgroundSnapshotLayer.backgroundColor = self.backgroundView.backgroundColor?.cgColor @@ -301,8 +331,10 @@ public final class PeerInfoCoverComponent: Component { } layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - let values: [NSNumber] = [1.0, 1.08, 1.0] - self.avatarBackgroundPatternContentsLayer.animateKeyframes(values: values, duration: 0.25, keyPath: "sublayerTransform.scale") + if bounce { + let values: [NSNumber] = [1.0, 1.14, 1.0] + self.avatarBackgroundPatternContentsLayer.animateKeyframes(values: values, duration: 0.4, keyPath: "sublayerTransform.scale") + } } private func loadPatternFromFile() { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift index 5d1481f5c1..99ae063a1a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift @@ -10,7 +10,6 @@ public enum PeerInfoPaneKey: Int32 { case botPreview case members case stories - case storyArchive case gifts case media case savedMessagesChats @@ -23,6 +22,51 @@ public enum PeerInfoPaneKey: Int32 { case groupsInCommon case similarChannels case similarBots + case storyArchive + + public init(tab: TelegramProfileTab) { + switch tab { + case .files: + self = .files + case .gifs: + self = .gifs + case .gifts: + self = .gifts + case .links: + self = .links + case .media: + self = .media + case .music: + self = .music + case .posts: + self = .stories + case .voice: + self = .voice + } + } + + public var tab: TelegramProfileTab? { + switch self { + case .stories: + return .posts + case .gifts: + return .gifts + case .media: + return .media + case .files: + return .files + case .music: + return .music + case .voice: + return .voice + case .links: + return .links + case .gifs: + return .gifs + default: + return nil + } + } } public struct PeerInfoStatusData: Equatable { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift index 07bbb69c5b..27ddd942a1 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent/Sources/PeerInfoRatingComponent.swift @@ -338,6 +338,10 @@ public final class PeerInfoRatingComponent: Component { context.clear(CGRect(origin: CGPoint(), size: size)) + if level < 0 { + return + } + if let url = Bundle.main.url(forResource: "profile_level\(levelIndex)_outer", withExtension: "svg"), let data = try? Data(contentsOf: url) { if let image = generateTintedImage(image: drawSvgImage(data, size, nil, nil, 0.0, false), color: component.borderColor) { image.draw(in: CGRect(origin: CGPoint(x: 0.0, y: backgroundOffsetsY[levelIndex] ?? 0.0), size: size), blendMode: .normal, alpha: 1.0) @@ -360,6 +364,13 @@ public final class PeerInfoRatingComponent: Component { context.clear(CGRect(origin: CGPoint(), size: size)) + if level < 0 { + if let image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/InlineRatingWarning"), color: component.backgroundColor) { + image.draw(in: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) * 0.5), y: floorToScreenPixels((size.height - image.size.height) * 0.5)), size: image.size)) + } + return + } + if let url = Bundle.main.url(forResource: "profile_level\(levelIndex)_inner", withExtension: "svg"), let data = try? Data(contentsOf: url) { if let image = generateTintedImage(image: drawSvgImage(data, size, nil, nil, 0.0, false), color: component.backgroundColor) { image.draw(in: CGRect(origin: CGPoint(x: 0.0, y: backgroundOffsetsY[levelIndex] ?? 0.0), size: size), blendMode: .normal, alpha: 1.0) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index e844a6c72e..9e56ec5e89 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -161,6 +161,10 @@ swift_library( "//submodules/Components/HierarchyTrackingLayer", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoRatingComponent", "//submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen", + "//submodules/TelegramUI/Components/TabSelectorComponent", + "//submodules/TelegramUI/Components/BottomButtonPanelComponent", + "//submodules/TelegramUI/Components/MarqueeComponent", + "//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index ba9e3802b9..e7ce0f314a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -127,7 +127,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { })) } - let presence: EnginePeer.Presence + var presence: EnginePeer.Presence if member.peer.id == context.account.peerId { presence = EnginePeer.Presence(status: .present(until: Int32.max), lastActivity: 0) } else if let value = member.presence { @@ -136,6 +136,17 @@ private enum PeerMembersListEntry: Comparable, Identifiable { presence = EnginePeer.Presence(status: .longTimeAgo, lastActivity: 0) } + var status: ContactsPeerItemStatus = .presence(presence, presentationData.dateTimeFormat) + if let user = member.peer as? TelegramUser, let botInfo = user.botInfo { + let botStatus: String + if botInfo.flags.contains(.hasAccessToChatHistory) { + botStatus = presentationData.strings.Bot_GroupStatusReadsHistory + } else { + botStatus = presentationData.strings.Bot_GroupStatusDoesNotReadHistory + } + status = .custom(string: NSAttributedString(string: botStatus, font: Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize * 14.0 / 17.0)), textColor: presentationData.theme.list.itemSecondaryTextColor), multiline: false, isActive: false, icon: nil) + } + return ContactsPeerItem( presentationData: ItemListPresentationData(presentationData), style: .plain, @@ -145,7 +156,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { context: context, peerMode: .memberList, peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)), - status: .presence(presence, presentationData.dateTimeFormat), + status: status, rightLabelText: label, enabled: true, selection: .none, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 61e7d41580..848d9d8a98 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -391,6 +391,8 @@ final class PeerInfoScreenData { let profileGiftsCollectionsContext: ProfileGiftsCollectionsContext? let premiumGiftOptions: [PremiumGiftCodeOption] let webAppPermissions: WebAppPermissionsState? + let savedMusicContext: ProfileSavedMusicContext? + let savedMusicState: ProfileSavedMusicContext.State? let _isContact: Bool var forceIsContact: Bool = false @@ -443,7 +445,9 @@ final class PeerInfoScreenData { profileGiftsContext: ProfileGiftsContext?, profileGiftsCollectionsContext: ProfileGiftsCollectionsContext?, premiumGiftOptions: [PremiumGiftCodeOption], - webAppPermissions: WebAppPermissionsState? + webAppPermissions: WebAppPermissionsState?, + savedMusicContext: ProfileSavedMusicContext?, + savedMusicState: ProfileSavedMusicContext.State? ) { self.peer = peer self.chatPeer = chatPeer @@ -485,6 +489,8 @@ final class PeerInfoScreenData { self.profileGiftsCollectionsContext = profileGiftsCollectionsContext self.premiumGiftOptions = premiumGiftOptions self.webAppPermissions = webAppPermissions + self.savedMusicContext = savedMusicContext + self.savedMusicState = savedMusicState } } @@ -1005,12 +1011,30 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, profileGiftsContext: profileGiftsContext, profileGiftsCollectionsContext: nil, premiumGiftOptions: [], - webAppPermissions: nil + webAppPermissions: nil, + savedMusicContext: nil, + savedMusicState: nil ) } } -func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?, existingProfileGiftsContext: ProfileGiftsContext?, existingProfileGiftsCollectionsContext: ProfileGiftsCollectionsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, privacySettings: Signal, forceHasGifts: Bool) -> Signal { +func peerInfoScreenData( + context: AccountContext, + peerId: PeerId, + strings: PresentationStrings, + dateTimeFormat: PresentationDateTimeFormat, + isSettings: Bool, + isMyProfile: Bool, + hintGroupInCommon: PeerId?, + existingRequestsContext: PeerInvitationImportersContext?, + existingProfileGiftsContext: ProfileGiftsContext?, + existingProfileGiftsCollectionsContext: ProfileGiftsCollectionsContext?, + chatLocation: ChatLocation, + chatLocationContextHolder: Atomic, + sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, + privacySettings: Signal, + forceHasGifts: Bool +) -> Signal { return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings) |> mapToSignal { inputData -> Signal in let wasUpgradedGroup = Atomic(value: nil) @@ -1057,7 +1081,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen profileGiftsContext: nil, profileGiftsCollectionsContext: nil, premiumGiftOptions: [], - webAppPermissions: nil + webAppPermissions: nil, + savedMusicContext: nil, + savedMusicState: nil )) case let .user(userPeerId, secretChatId, kind): let groupsInCommon: GroupsInCommonContext? @@ -1320,9 +1346,14 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen hasSavedMessageTags = .single(false) } - let starsRevenueContextAndState = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> mapToSignal { peer -> Signal<(StarsRevenueStatsContext?, StarsRevenueStats?), NoError> in - var canViewStarsRevenue = false + let starsRevenueContextAndState = combineLatest( + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> distinctUntilChanged, + context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.CanViewRevenue(id: peerId)) + |> distinctUntilChanged + ) + |> mapToSignal { peer, canViewRevenue -> Signal<(StarsRevenueStatsContext?, StarsRevenueStats?), NoError> in + var canViewStarsRevenue = canViewRevenue if let peer, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) || context.sharedContext.applicationBindings.appBuildType == .internal || context.sharedContext.immediateExperimentalUISettings.devRequests { canViewStarsRevenue = true } @@ -1372,6 +1403,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen return .single(nil) } } + + let savedMusicContext = ProfileSavedMusicContext(account: context.account, peerId: peerId) return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), @@ -1393,20 +1426,21 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen starsRevenueContextAndState, revenueContextAndState, premiumGiftOptions, - webAppPermissions + webAppPermissions, + savedMusicContext.state ) - |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, hasStoryArchive, recommendedBots, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, hasBotPreviewItems, personalChannel, privacySettings, starsRevenueContextAndState, revenueContextAndState, premiumGiftOptions, webAppPermissions -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, hasStoryArchive, recommendedBots, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, hasBotPreviewItems, personalChannel, privacySettings, starsRevenueContextAndState, revenueContextAndState, premiumGiftOptions, webAppPermissions, savedMusicState -> PeerInfoScreenData in var availablePanes = availablePanes if isMyProfile { availablePanes?.insert(.stories, at: 0) - if let hasStoryArchive, hasStoryArchive { - availablePanes?.insert(.storyArchive, at: 1) - } if availablePanes != nil, profileGiftsContext != nil, let cachedData = peerView.cachedData as? CachedUserData { if let starGiftsCount = cachedData.starGiftsCount, starGiftsCount > 0 { - availablePanes?.insert(.gifts, at: hasStoryArchive == true ? 2 : 1) + availablePanes?.insert(.gifts, at: 1) } } + if let hasStoryArchive, hasStoryArchive { + availablePanes?.append(.storyArchive) + } } else if let hasStories { if hasStories, peerView.peers[peerView.peerId] is TelegramUser, peerView.peerId != context.account.peerId { availablePanes?.insert(.stories, at: 0) @@ -1454,6 +1488,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen availablePanes = nil } + if var currentAvailablePanes = availablePanes, let cachedData = peerView.cachedData as? CachedUserData, let mainProfileTab = cachedData.mainProfileTab { + let mainTabKey = PeerInfoPaneKey(tab: mainProfileTab) + if currentAvailablePanes.contains(mainTabKey) && currentAvailablePanes.first != mainTabKey { + currentAvailablePanes = currentAvailablePanes.filter { $0 != mainTabKey } + currentAvailablePanes.insert(mainTabKey, at: 0) + availablePanes = currentAvailablePanes + } + } + let peer = peerView.peers[userPeerId] var globalSettings: TelegramGlobalSettings? @@ -1522,7 +1565,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen profileGiftsContext: profileGiftsContext, profileGiftsCollectionsContext: profileGiftsCollectionsContext, premiumGiftOptions: premiumGiftOptions, - webAppPermissions: webAppPermissions + webAppPermissions: webAppPermissions, + savedMusicContext: savedMusicContext, + savedMusicState: savedMusicState ) } case .channel: @@ -1686,6 +1731,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen availablePanes = nil } + if var currentAvailablePanes = availablePanes, let cachedData = peerView.cachedData as? CachedChannelData, let mainProfileTab = cachedData.mainProfileTab { + let mainTabKey = PeerInfoPaneKey(tab: mainProfileTab) + if currentAvailablePanes.contains(mainTabKey) && currentAvailablePanes.first != mainTabKey { + currentAvailablePanes = currentAvailablePanes.filter { $0 != mainTabKey } + currentAvailablePanes.insert(mainTabKey, at: 0) + availablePanes = currentAvailablePanes + } + } + var discussionPeer: Peer? if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { discussionPeer = peer @@ -1756,7 +1810,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen profileGiftsContext: profileGiftsContext, profileGiftsCollectionsContext: profileGiftsCollectionsContext, premiumGiftOptions: [], - webAppPermissions: nil + webAppPermissions: nil, + savedMusicContext: nil, + savedMusicState: nil ) } case let .group(groupId): @@ -2090,7 +2146,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen profileGiftsContext: nil, profileGiftsCollectionsContext: nil, premiumGiftOptions: [], - webAppPermissions: nil + webAppPermissions: nil, + savedMusicContext: nil, + savedMusicState: nil )) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 6d8ad714ce..59df90ccd2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -41,6 +41,9 @@ import MultilineTextComponent import PeerInfoRatingComponent import UndoUI import ProfileLevelInfoScreen +import PlainButtonComponent +import BundleIconComponent +import MarqueeComponent final class PeerInfoHeaderNavigationTransition { let sourceNavigationBar: NavigationBar @@ -160,6 +163,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { let editingNavigationBackgroundNode: NavigationBackgroundNode let editingNavigationBackgroundSeparator: ASDisplayNode + var musicBackground: UIView? + var music: ComponentView? + var performButtonAction: ((PeerInfoHeaderButtonKey, ContextGesture?) -> Void)? var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)? var requestOpenAvatarForEditing: ((Bool) -> Void)? @@ -171,6 +177,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? var displayEmojiPackTooltip: (() -> Void)? + var displaySavedMusic: (() -> Void)? + var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)? var displayStatusPremiumIntro: (() -> Void)? var displayUniqueGiftInfo: ((UIView, String) -> Void)? @@ -307,7 +315,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.animationRenderer = context.animationRenderer super.init() - + requestUpdateLayoutImpl = { [weak self] in self?.requestUpdateLayout?(false) } @@ -501,7 +509,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var currentStatusIcon: CredibilityIcon? private var currentPanelStatusData: PeerInfoStatusData? - func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, profileGiftsContext: ProfileGiftsContext?, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { + func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, profileGiftsContext: ProfileGiftsContext?, screenData: PeerInfoScreenData?, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { if self.appliedCustomNavigationContentNode !== self.customNavigationContentNode { if let previous = self.appliedCustomNavigationContentNode { transition.updateAlpha(node: previous, alpha: 0.0, completion: { [weak previous] _ in @@ -549,6 +557,48 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + let actionButtonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact) + let buttonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info) + + let backgroundCoverSubject: PeerInfoCoverComponent.Subject? + var backgroundCoverAnimateIn = false + var backgroundDefaultHeight: CGFloat = 254.0 + var hasBackground = false + if let status = peer?.emojiStatus, case .starGift = status.content { + backgroundCoverSubject = .status(status) + if !self.didSetupBackgroundCover { + if !self.isSettings { + backgroundCoverAnimateIn = true + } + self.didSetupBackgroundCover = true + } + if !buttonKeys.isEmpty { + backgroundDefaultHeight = 327.0 + if metrics.isTablet { + backgroundDefaultHeight += 60.0 + } + } + hasBackground = true + } else if let peer { + backgroundCoverSubject = .peer(EnginePeer(peer)) + if peer.profileColor != nil { + hasBackground = true + } + } else { + backgroundCoverSubject = nil + } + + var currentSavedMusic: TelegramMediaFile? + if !self.isSettings, let screenData { + if let savedMusicState = screenData.savedMusicState { + currentSavedMusic = savedMusicState.files.first + } else if let cachedUserData = screenData.cachedData as? CachedUserData { + currentSavedMusic = cachedUserData.savedMusic + } + } + let musicHeight: CGFloat = hasBackground || self.isAvatarExpanded ? 24.0 : 16.0 + let bottomInset: CGFloat = currentSavedMusic != nil ? musicHeight : 0.0 + let isLandscape = containerInset > 16.0 let themeUpdated = self.presentationData?.theme !== presentationData.theme @@ -620,11 +670,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { let regularContentButtonBackgroundColor: UIColor let collapsedHeaderContentButtonBackgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - let expandedAvatarContentButtonBackgroundColor: UIColor = UIColor(white: 0.0, alpha: 0.1) + let expandedAvatarContentButtonBackgroundColor: UIColor = UIColor(white: 1.0, alpha: 0.1) let regularHeaderButtonBackgroundColor: UIColor let collapsedHeaderButtonBackgroundColor: UIColor = .clear - let expandedAvatarHeaderButtonBackgroundColor: UIColor = UIColor(white: 0.0, alpha: 0.1) + let expandedAvatarHeaderButtonBackgroundColor: UIColor = UIColor(white: 1.0, alpha: 0.1) let regularContentButtonForegroundColor: UIColor = peer?.profileColor != nil ? UIColor.white : presentationData.theme.list.itemAccentColor let collapsedHeaderContentButtonForegroundColor = presentationData.theme.list.itemAccentColor @@ -781,6 +831,13 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.avatarClippingNode.clipsToBounds = true } + let accentRatingBackgroundColor: UIColor + if let currentStarRating = self.currentStarRating, currentStarRating.level < 0 { + accentRatingBackgroundColor = UIColor(rgb: 0xFF3B30) + } else { + accentRatingBackgroundColor = presentationData.theme.list.itemCheckColors.fillColor + } + let ratingBackgroundColor: UIColor let ratingBorderColor: UIColor let ratingForegroundColor: UIColor @@ -796,7 +853,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { headerButtonBackgroundColor = collapsedHeaderButtonBackgroundColor - ratingBackgroundColor = presentationData.theme.list.itemCheckColors.fillColor + ratingBackgroundColor = accentRatingBackgroundColor ratingBorderColor = .clear ratingForegroundColor = presentationData.theme.list.itemCheckColors.foregroundColor } else if self.isAvatarExpanded { @@ -833,7 +890,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if let status = peer?.emojiStatus, case let .starGift(_, _, _, _, _, innerColor, outerColor, patternColorValue, _) = status.content { let _ = innerColor - ratingBackgroundColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(presentationData.theme.list.itemCheckColors.fillColor, alpha: effectiveTransitionFraction) + ratingBackgroundColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(accentRatingBackgroundColor, alpha: effectiveTransitionFraction) let innerColor = UIColor(rgb: UInt32(bitPattern: innerColor)) let outerColor = UIColor(rgb: UInt32(bitPattern: outerColor)) @@ -855,29 +912,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { ratingBorderColor = patternColor.withAlphaComponent(0.1).blendOver(background: backgroundColor).mixedWith(.clear, alpha: effectiveTransitionFraction) ratingForegroundColor = ratingBorderColor.mixedWith(presentationData.theme.list.itemCheckColors.foregroundColor, alpha: effectiveTransitionFraction) } else { - ratingBackgroundColor = presentationData.theme.list.itemCheckColors.fillColor + ratingBackgroundColor = accentRatingBackgroundColor ratingBorderColor = UIColor.clear ratingForegroundColor = presentationData.theme.list.itemCheckColors.foregroundColor } - - /*if let status = peer?.emojiStatus, case let .starGift(_, _, _, _, _, innerColor, outerColor, _, _) = status.content { - let _ = outerColor - let mainColor = UIColor(rgb: UInt32(bitPattern: innerColor)) - - ratingBackgroundColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(presentationData.theme.list.itemCheckColors.fillColor, alpha: effectiveTransitionFraction) - ratingForegroundColor = mainColor.withMultiplied(hue: 1.0, saturation: 1.1, brightness: 0.9).mixedWith(UIColor.clear, alpha: effectiveTransitionFraction) - ratingBorderColor = ratingForegroundColor.mixedWith(presentationData.theme.list.itemCheckColors.foregroundColor, alpha: effectiveTransitionFraction) - } else if let profileColor = peer?.profileColor { - let backgroundColors = self.context.peerNameColors.getProfile(profileColor, dark: presentationData.theme.overallDarkAppearance) - - ratingBackgroundColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(presentationData.theme.list.itemCheckColors.fillColor, alpha: effectiveTransitionFraction) - ratingForegroundColor = backgroundColors.main.withMultiplied(hue: 1.0, saturation: 1.1, brightness: 0.9).mixedWith(UIColor.clear, alpha: effectiveTransitionFraction) - ratingBorderColor = ratingForegroundColor.mixedWith(presentationData.theme.list.itemCheckColors.foregroundColor, alpha: effectiveTransitionFraction) - } else { - ratingBackgroundColor = presentationData.theme.list.itemCheckColors.fillColor - ratingBorderColor = UIColor.clear - ratingForegroundColor = presentationData.theme.list.itemCheckColors.foregroundColor - }*/ } do { @@ -1179,10 +1217,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight) - - let actionButtonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact) - let buttonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info) - + var isPremium = false var isVerified = false var isFake = false @@ -1275,15 +1310,18 @@ final class PeerInfoHeaderNode: ASDisplayNode { subtitleColor = UIColor.white let statusText: String - statusText = peer.debugDisplayTitle + if let channel = peer as? TelegramChannel, channel.linkedBotId != nil { + statusText = " " + } else { + statusText = peer.debugDisplayTitle + subtitleIsButton = true + } subtitleStringText = statusText subtitleAttributes = MultiScaleTextState.Attributes(font: Font.semibold(16.0), color: subtitleColor) smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor) usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white)) - - subtitleIsButton = true let (maybePanelStatusData, _, _) = panelStatusData if let panelStatusData = maybePanelStatusData { @@ -1442,7 +1480,15 @@ final class PeerInfoHeaderNode: ASDisplayNode { if let previousPanelStatusData = previousPanelStatusData, let currentPanelStatusData = panelStatusData.0, let previousPanelStatusDataKey = previousPanelStatusData.key, let currentPanelStatusDataKey = currentPanelStatusData.key, previousPanelStatusDataKey != currentPanelStatusDataKey { if let snapshotView = self.panelSubtitleNode.view.snapshotContentTree() { - let direction: CGFloat = previousPanelStatusDataKey.rawValue > currentPanelStatusDataKey.rawValue ? 1.0 : -1.0 + let previousIndex = screenData?.availablePanes.firstIndex(of: previousPanelStatusDataKey) + let currentIndex = screenData?.availablePanes.firstIndex(of: currentPanelStatusDataKey) + + let direction: CGFloat + if let previousIndex, let currentIndex { + direction = previousIndex > currentIndex ? 1.0 : -1.0 + } else { + direction = previousPanelStatusDataKey.rawValue > currentPanelStatusDataKey.rawValue ? 1.0 : -1.0 + } self.panelSubtitleNode.view.superview?.addSubview(snapshotView) snapshotView.frame = self.panelSubtitleNode.frame @@ -1599,7 +1645,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isAvatarExpanded { let minTitleSize = CGSize(width: titleSize.width * expandedTitleScale, height: titleSize.height * expandedTitleScale) - var minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) + var minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - bottomInset - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) if !self.isSettings && !self.isMyProfile { minTitleFrame.origin.y -= 83.0 } @@ -1972,6 +2018,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { expandablePart += 99.0 } } + expandablePart += bottomInset height = navigationHeight + max(0.0, expandablePart) maxY = navigationHeight + panelWithAvatarHeight - contentOffset backgroundHeight = height @@ -1986,20 +2033,30 @@ final class PeerInfoHeaderNode: ASDisplayNode { if let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating { self.currentStarRating = starRating self.currentPendingStarRating = cachedData.pendingStarRating - - #if DEBUG - if let _ = starRating.nextLevelStars { - self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level, currentLevelStars: starRating.currentLevelStars, stars: starRating.stars + 234, nextLevelStars: starRating.nextLevelStars), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3) - self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level + 1, currentLevelStars: starRating.nextLevelStars!, stars: starRating.nextLevelStars! + starRating.nextLevelStars! / 2 + starRating.nextLevelStars! / 4, nextLevelStars: starRating.nextLevelStars! * 2), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3) - } - #endif } else { self.currentStarRating = nil self.currentPendingStarRating = nil } - if let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating { - //if "".isEmpty { + #if DEBUG && false + if "".isEmpty { + let starRating: TelegramStarRating + + if self.context.account.peerId.id._internalGetInt64Value() == 654152421 { + starRating = TelegramStarRating(level: -1, currentLevelStars: -1, stars: -100, nextLevelStars: 0) + } else { + starRating = TelegramStarRating(level: 2, currentLevelStars: 1000, stars: 2000, nextLevelStars: 3000) + } + self.currentStarRating = starRating + + if let _ = starRating.nextLevelStars { + //self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level, currentLevelStars: starRating.currentLevelStars, stars: starRating.stars + 234, nextLevelStars: starRating.nextLevelStars), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3) + self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level + 2, currentLevelStars: starRating.nextLevelStars!, stars: max(500, starRating.nextLevelStars! + starRating.nextLevelStars! / 2 - starRating.nextLevelStars! / 4), nextLevelStars: max(1000, starRating.nextLevelStars! * 2)), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3) + } + } + #endif + + if let starRating = self.currentStarRating { let subtitleRating: ComponentView var subtitleRatingTransition = ComponentTransition(transition) if let current = self.subtitleRating { @@ -2018,7 +2075,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { foregroundColor: ratingForegroundColor, level: Int(starRating.level), action: { [weak self] in - guard let self, let peer, let currentStarRating = self.currentStarRating else { + guard let self, let peer = self.peer, let currentStarRating = self.currentStarRating else { return } self.controller?.push(ProfileLevelInfoScreen( @@ -2092,7 +2149,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let subtitleBadgeFrame: CGRect subtitleBadgeFrame = CGRect(origin: CGPoint(x: (-subtitleSize.width) * 0.5 - subtitleRatingSize.width + 1.0, y: subtitleOffset + floor((-subtitleRatingSize.height) * 0.5)), size: subtitleRatingSize) transition.updateFrameAdditive(view: subtitleRatingView, frame: subtitleBadgeFrame) - transition.updateAlpha(layer: subtitleRatingView.layer, alpha: subtitleAlpha) + transition.updateAlpha(layer: subtitleRatingView.layer, alpha: subtitleAlpha * (1.0 - transitionFraction)) } } else { let titleScale: CGFloat @@ -2270,12 +2327,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { let buttonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(buttonKeys.count) - buttonSpacing let buttonSize = CGSize(width: buttonWidth, height: 58.0) - var buttonRightOrigin = CGPoint(x: width - buttonSideInset, y: backgroundHeight - 16.0 - buttonSize.height) + var buttonRightOrigin = CGPoint(x: width - buttonSideInset, y: backgroundHeight - bottomInset - 16.0 - buttonSize.height) if !actionButtonKeys.isEmpty { buttonRightOrigin.y += actionButtonSize.height + 24.0 } - transition.updateFrameAdditive(node: self.buttonsBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonRightOrigin.y), size: CGSize(width: width, height: buttonSize.height))) + transition.updateFrameAdditive(node: self.buttonsBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonRightOrigin.y), size: CGSize(width: width, height: buttonSize.height + 40.0))) self.buttonsBackgroundNode.update(size: self.buttonsBackgroundNode.bounds.size, transition: transition) self.buttonsBackgroundNode.updateColor(color: contentButtonBackgroundColor, enableBlur: true, transition: transition) @@ -2397,7 +2454,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isAvatarExpanded { resolvedRegularHeight = expandedAvatarListSize.height } else { - resolvedRegularHeight = panelWithAvatarHeight + navigationHeight + resolvedRegularHeight = panelWithAvatarHeight + navigationHeight + bottomInset } let backgroundFrame: CGRect @@ -2429,35 +2486,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } else { transition.updateFrame(view: self.backgroundBannerView, frame: bannerFrame) } - - let backgroundCoverSubject: PeerInfoCoverComponent.Subject? - var backgroundCoverAnimateIn = false - var backgroundDefaultHeight: CGFloat = 254.0 - var hasBackground = false - if let status = peer?.emojiStatus, case .starGift = status.content { - backgroundCoverSubject = .status(status) - if !self.didSetupBackgroundCover { - if !self.isSettings { - backgroundCoverAnimateIn = true - } - self.didSetupBackgroundCover = true - } - if !buttonKeys.isEmpty { - backgroundDefaultHeight = 327.0 - if metrics.isTablet { - backgroundDefaultHeight += 60.0 - } - } - hasBackground = true - } else if let peer { - backgroundCoverSubject = .peer(EnginePeer(peer)) - if peer.profileColor != nil { - hasBackground = true - } - } else { - backgroundCoverSubject = nil - } - + let backgroundCoverSize = self.backgroundCover.update( transition: ComponentTransition(transition), component: AnyComponent(PeerInfoCoverComponent( @@ -2578,6 +2607,140 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + if let currentSavedMusic { + var musicTransition = transition + var artist = presentationData.strings.MediaPlayer_UnknownArtist + var track: String? + for attribute in currentSavedMusic.attributes { + if case let .Audio(_, _, title, performer, _) = attribute { + artist = performer ?? artist + track = title + break + } + } + if track == nil { + if let fileName = currentSavedMusic.fileName { + track = fileName + } else { + track = presentationData.strings.MediaPlayer_UnknownTrack + } + } + + if hasBackground || self.isAvatarExpanded { + if self.musicBackground == nil { + musicTransition = .immediate + } + let musicBackground = self.musicBackground ?? { + let musicBackground = UIView() + musicBackground.backgroundColor = .white + self.buttonsMaskView.addSubview(musicBackground) + self.musicBackground = musicBackground + if transition.isAnimated { + musicBackground.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + return musicBackground + }() + musicTransition.updateFrame(view: musicBackground, frame: CGRect(origin: CGPoint(x: 0.0, y: backgroundHeight - 24.0 - buttonRightOrigin.y), size: CGSize(width: backgroundFrame.width, height: 24.0))) + + if let _ = self.navigationTransition { + transition.updateAlpha(layer: musicBackground.layer, alpha: 1.0 - transitionFraction) + } else { + musicTransition.updateAlpha(layer: musicBackground.layer, alpha: 1.0) + } + } else if let musicBackground = self.musicBackground { + self.musicBackground = nil + if transition.isAnimated { + musicBackground.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + musicBackground.removeFromSuperview() + }) + } else { + musicBackground.removeFromSuperview() + } + } + + let music = self.music ?? { + let componentView = ComponentView() + self.music = componentView + return componentView + }() + + let musicString = NSMutableAttributedString() + let isOverlay = self.isAvatarExpanded || hasBackground + musicString.append(NSAttributedString(string: track ?? "", font: Font.semibold(12.0), textColor: isOverlay ? .white : presentationData.theme.list.itemAccentColor)) + musicString.append(NSAttributedString(string: " - \(artist)", font: Font.regular(12.0), textColor: isOverlay ? UIColor.white.withAlphaComponent(0.7) : presentationData.theme.list.itemSecondaryTextColor)) + + let musicSize = music.update( + transition: .immediate, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent( + HStack([ + AnyComponentWithIdentity( + id: "icon", + component: AnyComponent(BundleIconComponent(name: "Media Editor/SmallAudio", tintColor: isOverlay ? .white : presentationData.theme.list.itemAccentColor)) + ), + AnyComponentWithIdentity( + id: "label", + component: AnyComponent(MarqueeComponent(attributedText: musicString, maxWidth: backgroundFrame.width - 96.0)) + ), + AnyComponentWithIdentity( + id: "arrow", + component: AnyComponent(BundleIconComponent(name: "Item List/InlineTextRightArrow", tintColor: isOverlay ? .white : presentationData.theme.list.itemSecondaryTextColor)) + ) + ], spacing: 4.0) + ), + minSize: CGSize(width: backgroundFrame.width, height: musicHeight), + action: { [weak self] in + self?.displaySavedMusic?() + } + ) + ), + environment: {}, + containerSize: CGSize(width: backgroundFrame.width, height: musicHeight) + ) + let musicFrame = CGRect(origin: CGPoint(x: 0.0, y: (apparentBackgroundHeight - backgroundHeight) + backgroundHeight - musicHeight - (hasBackground || self.isAvatarExpanded ? 0.0 : 4.0)), size: musicSize) + if let musicView = music.view { + if musicView.superview == nil { + self.regularContentNode.view.addSubview(musicView) + if transition.isAnimated { + musicView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + if additive { + musicTransition.updateFrameAdditiveToCenter(view: musicView, frame: musicFrame) + } else { + musicTransition.updateFrame(view: musicView, frame: musicFrame) + } + + if let _ = self.navigationTransition { + transition.updateAlpha(layer: musicView.layer, alpha: 1.0 - transitionFraction) + } else { + musicTransition.updateAlpha(layer: musicView.layer, alpha: backgroundBannerAlpha) + } + } + } else { + if let musicBackground = self.musicBackground { + self.musicBackground = nil + if transition.isAnimated { + musicBackground.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + musicBackground.removeFromSuperview() + }) + } else { + musicBackground.removeFromSuperview() + } + } + if let music = self.music { + self.music = nil + if transition.isAnimated { + music.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + music.view?.removeFromSuperview() + }) + } else { + music.view?.removeFromSuperview() + } + } + } + if isFirstTime { self.updateAvatarMask(transition: .immediate) } @@ -2593,6 +2756,14 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.performButtonAction?(buttonNode.key, gesture) } + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + var result = super.point(inside: point, with: event) + if let musicView = self.music?.view, musicView.frame.contains(point) { + result = true + } + return result + } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard let result = super.hitTest(point, with: event) else { return nil @@ -2672,6 +2843,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { return giftsCoverView } + if let musicView = self.music?.view, let result = musicView.hitTest(self.view.convert(point, to: musicView), with: event) { + return result + } + if result == self.view || result == self.regularContentNode.view || result == self.editingContentNode.view { return nil } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index 97512e4a65..47d81e1bcd 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -15,6 +15,11 @@ import PeerInfoChatListPaneNode import PeerInfoChatPaneNode import TextFormat import EmojiTextAttachmentView +import ComponentFlow +import TabSelectorComponent +import MultilineTextComponent +import BottomButtonPanelComponent +import UndoUI final class PeerInfoPaneWrapper { let key: PeerInfoPaneKey @@ -38,6 +43,176 @@ final class PeerInfoPaneWrapper { } } +private final class GiftsTabItemComponent: Component { + typealias EnvironmentType = TabSelectorComponent.ItemEnvironment + + let context: AccountContext + let icons: [ProfileGiftsContext.State.StarGift] + let title: String + let theme: PresentationTheme + + init( + context: AccountContext, + icons: [ProfileGiftsContext.State.StarGift], + title: String, + theme: PresentationTheme + ) { + self.context = context + self.icons = icons + self.title = title + self.theme = theme + } + + static func ==(lhs: GiftsTabItemComponent, rhs: GiftsTabItemComponent) -> Bool { + if lhs.icons != rhs.icons { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.theme !== rhs.theme { + return false + } + return true + } + + final class View: UIView { + private let title = ComponentView() + private let icon = ComponentView() + private var iconLayers: [AnyHashable: InlineStickerItemLayer] = [:] + + private var component: GiftsTabItemComponent? + + func update(component: GiftsTabItemComponent, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let environment = environment[EnvironmentType.self].value + + let textSpacing: CGFloat = 2.0 + let iconSpacing: CGFloat = 1.0 + + let normalColor = component.theme.list.itemSecondaryTextColor + let selectedColor = component.theme.list.itemAccentColor + let effectiveColor = normalColor.mixedWith(selectedColor, alpha: environment.selectionFraction) + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: effectiveColor)) + )), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 100.0) + ) + + var iconOffset: CGFloat = titleSize.width + textSpacing + var iconsWidth: CGFloat = 0.0 + if !component.icons.isEmpty { + iconsWidth += iconSpacing + var validIds = Set() + var index = 0 + for icon in component.icons { + let id: AnyHashable + if let reference = icon.reference { + id = reference + } else { + id = index + } + validIds.insert(id) + + let iconSize = CGSize(width: 18.0, height: 18.0) + let animationLayer: InlineStickerItemLayer + if let current = self.iconLayers[id] { + animationLayer = current + } else { + var file: TelegramMediaFile? + switch icon.gift { + case let .generic(gift): + file = gift.file + case let .unique(gift): + for attribute in gift.attributes { + if case let .model(_, fileValue, _) = attribute { + file = fileValue + } + } + } + guard let file else { + continue + } + + let emoji = ChatTextInputTextCustomEmojiAttribute( + interactivelySelectedFromPackId: nil, + fileId: file.fileId.id, + file: file + ) + animationLayer = InlineStickerItemLayer( + context: .account(component.context), + userLocation: .other, + attemptSynchronousLoad: false, + emoji: emoji, + file: file, + cache: component.context.animationCache, + renderer: component.context.animationRenderer, + unique: true, + placeholderColor: component.theme.list.mediaPlaceholderColor, + pointSize: iconSize, + loopCount: 1 + ) + animationLayer.isVisibleForAnimations = true + self.iconLayers[id] = animationLayer + self.layer.addSublayer(animationLayer) + + animationLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + animationLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + } + transition.setFrame(layer: animationLayer, frame: CGRect(origin: CGPoint(x: iconOffset, y: 0.0), size: iconSize)) + iconOffset += iconSize.width + iconSpacing + iconsWidth += iconSize.width + iconSpacing + + index += 1 + } + + var removeIds: [AnyHashable] = [] + for (id, layer) in self.iconLayers { + if !validIds.contains(id) { + removeIds.append(id) + layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false) + layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + layer.removeFromSuperlayer() + }) + } + } + for id in removeIds { + self.iconLayers.removeValue(forKey: id) + } + } else { + for (_, layer) in self.iconLayers { + layer.removeFromSuperlayer() + } + self.iconLayers.removeAll() + } + + let titleFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.addSubview(titleView) + } + titleView.frame = titleFrame + } + + return CGSize(width: titleSize.width + iconsWidth, height: titleSize.height) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + final class PeerInfoPaneTabsContainerPaneNode: ASDisplayNode { private let pressed: () -> Void @@ -702,8 +877,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat private let coveringBackgroundNode: NavigationBackgroundNode private let additionalBackgroundNode: ASDisplayNode private let separatorNode: ASDisplayNode - private let tabsContainerNode: PeerInfoPaneTabsContainerNode + private let tabsContainer = ComponentView() private let tabsSeparatorNode: ASDisplayNode + private var didJustReorderTabs = false + + private var actionPanel: ComponentView? let isReady = Promise() var didSetIsReady = false @@ -780,52 +958,15 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat self.coveringBackgroundNode = NavigationBackgroundNode(color: .clear) self.coveringBackgroundNode.isUserInteractionEnabled = false - - self.tabsContainerNode = PeerInfoPaneTabsContainerNode(context: context) - + self.tabsSeparatorNode = ASDisplayNode() - self.tabsSeparatorNode.isLayerBacked = true super.init() // self.addSubnode(self.separatorNode) self.addSubnode(self.additionalBackgroundNode) self.addSubnode(self.coveringBackgroundNode) - self.addSubnode(self.tabsContainerNode) self.addSubnode(self.tabsSeparatorNode) - - self.tabsContainerNode.requestSelectPane = { [weak self] key in - guard let strongSelf = self else { - return - } - if strongSelf.currentPaneKey == key { - if let requestExpandTabs = strongSelf.requestExpandTabs, requestExpandTabs() { - } else { - let _ = strongSelf.currentPane?.node.scrollToTop() - } - return - } - if strongSelf.currentPanes[key] != nil { - strongSelf.currentPaneKey = key - - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) - - strongSelf.currentPaneUpdated?(true) - - strongSelf.currentPaneStatusPromise.set(strongSelf.currentPane?.node.status ?? .single(nil)) - strongSelf.nextPaneStatusPromise.set(.single(nil)) - strongSelf.paneTransitionPromise.set(nil) - } - } else if strongSelf.pendingSwitchToPaneKey != key { - strongSelf.pendingSwitchToPaneKey = key - strongSelf.expandOnSwitch = true - - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) - } - } - } } override func didLoad() { @@ -844,7 +985,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat guard let currentPaneKey = strongSelf.currentPaneKey, let availablePanes = currentParams.data?.availablePanes, let index = availablePanes.firstIndex(of: currentPaneKey) else { return [] } - if strongSelf.tabsContainerNode.bounds.contains(strongSelf.view.convert(point, to: strongSelf.tabsContainerNode.view)) { + if let tabsContainerView = strongSelf.tabsContainer.view, tabsContainerView.bounds.contains(strongSelf.view.convert(point, to: tabsContainerView)) { return [] } if case .savedMessagesChats = currentPaneKey { @@ -1017,6 +1158,46 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } } + func openTabContextMenu(key: PeerInfoPaneKey, sourceNode: ASDisplayNode, gesture: ContextGesture?) { + guard let params = self.currentParams, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else { + return + } + + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Tabs_SetMainTab, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + + guard let tab = key.tab else { + return + } + Queue.mainQueue().after(0.15) { + self.didJustReorderTabs = true + let _ = (self.context.engine.peers.setMainProfileTab(peerId: self.peerId, tab: tab) + |> deliverOnMainQueue).start(completed: { [weak self] in + guard let self else { + return + } + let controller = UndoOverlayController(presentationData: params.presentationData, content: .actionSucceeded(title: nil, text: params.presentationData.strings.PeerInfo_Tabs_SetMainTab_Succeed, cancel: nil, destructive: false), action: { _ in return true }) + self.parentController?.present(controller, in: .current) + }) + } + }))) + + let contextController = ContextController( + presentationData: params.presentationData, + source: .extracted(TabsExtractedContentSource(sourceNode: sourceNode)), + items: .single(ContextController.Items(content: .list(items))), + recognizer: nil, + gesture: gesture + ) + self.parentController?.presentInGlobalOverlay(contextController) + } + func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, disableTabSwitching: Bool, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { let previousAvailablePanes = self.currentAvailablePanes let availablePanes = data?.availablePanes ?? [] @@ -1240,7 +1421,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat paneDefaultTransition = .immediate } - + + if self.didJustReorderTabs && previousAvailablePanes != availablePanes { + self.didJustReorderTabs = false + paneDefaultTransition = .immediate + } + if let _ = data { if let previousAvailablePanes = previousAvailablePanes, previousAvailablePanes.isEmpty, !availablePanes.isEmpty { self.shouldFadeIn = true @@ -1323,59 +1509,145 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat tabsAlpha = 1.0 - tabsOffset / tabsHeight } tabsAlpha *= tabsAlpha - transition.updateFrame(node: self.tabsContainerNode, frame: CGRect(origin: CGPoint(x: sideInset, y: -tabsOffset), size: CGSize(width: size.width - sideInset * 2.0, height: tabsHeight))) - transition.updateAlpha(node: self.tabsContainerNode, alpha: tabsAlpha) - + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel))) transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel))) transition.updateFrame(node: self.additionalBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel))) self.coveringBackgroundNode.update(size: self.coveringBackgroundNode.bounds.size, transition: transition) transition.updateFrame(node: self.tabsSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: tabsHeight - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel))) - - self.tabsContainerNode.update(size: CGSize(width: size.width - sideInset * 2.0, height: tabsHeight), presentationData: presentationData, paneList: availablePanes.map { key in - let title: String - var icons: [ProfileGiftsContext.State.StarGift] = [] + + var canManageTabs = false + if let peer = data?.peer { + if peer.id == self.context.account.peerId { + canManageTabs = true + } else if let channel = data?.peer as? TelegramChannel, case .broadcast = channel.info { + if channel.hasPermission(.changeInfo) { + canManageTabs = true + } + } + } + + let items: [TabSelectorComponent.Item] = availablePanes.map { key in + let content: TabSelectorComponent.Item.Content + var canReorder = false switch key { case .stories: - title = presentationData.strings.PeerInfo_PaneStories + content = .text(presentationData.strings.PeerInfo_PaneStories) + canReorder = true case .storyArchive: - title = presentationData.strings.PeerInfo_PaneArchivedStories + content = .text(presentationData.strings.PeerInfo_PaneArchivedStories) case .botPreview: - title = presentationData.strings.PeerInfo_PaneBotPreviews + content = .text(presentationData.strings.PeerInfo_PaneBotPreviews) case .media: - title = presentationData.strings.PeerInfo_PaneMedia + content = .text(presentationData.strings.PeerInfo_PaneMedia) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .files: - title = presentationData.strings.PeerInfo_PaneFiles + content = .text(presentationData.strings.PeerInfo_PaneFiles) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .links: - title = presentationData.strings.PeerInfo_PaneLinks + content = .text(presentationData.strings.PeerInfo_PaneLinks) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .voice: - title = presentationData.strings.PeerInfo_PaneVoiceAndVideo + content = .text(presentationData.strings.PeerInfo_PaneVoiceAndVideo) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .gifs: - title = presentationData.strings.PeerInfo_PaneGifs + content = .text(presentationData.strings.PeerInfo_PaneGifs) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .music: - title = presentationData.strings.PeerInfo_PaneAudio + content = .text(presentationData.strings.PeerInfo_PaneAudio) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel case .groupsInCommon: - title = presentationData.strings.PeerInfo_PaneGroups + content = .text(presentationData.strings.PeerInfo_PaneGroups) case .members: - title = presentationData.strings.PeerInfo_PaneMembers + content = .text(presentationData.strings.PeerInfo_PaneMembers) case .similarChannels: - title = presentationData.strings.PeerInfo_PaneRecommended + content = .text(presentationData.strings.PeerInfo_PaneRecommended) case .similarBots: - title = presentationData.strings.PeerInfo_PaneRecommendedBots + content = .text(presentationData.strings.PeerInfo_PaneRecommendedBots) case .savedMessagesChats: - title = presentationData.strings.DialogList_TabTitle + content = .text(presentationData.strings.DialogList_TabTitle) case .savedMessages: - title = presentationData.strings.PeerInfo_SavedMessagesTabTitle + content = .text(presentationData.strings.PeerInfo_SavedMessagesTabTitle) case .gifts: - title = presentationData.strings.PeerInfo_PaneGifts + var icons: [ProfileGiftsContext.State.StarGift] = [] if let gifts = data?.profileGiftsContext?.currentState?.gifts.prefix(3) { icons = Array(gifts) } + content = .component(AnyComponent( + GiftsTabItemComponent(context: self.context, icons: icons, title: presentationData.strings.PeerInfo_PaneGifts, theme: presentationData.theme) + )) + canReorder = true } - return PeerInfoPaneSpecifier(key: key, title: title, icons: icons) - }, selectedPane: self.currentPaneKey, disableSwitching: disableTabSwitching, transitionFraction: self.transitionFraction, transition: transition) + return TabSelectorComponent.Item(id: key, content: content, isReorderable: false, contextAction: key != availablePanes.first && canManageTabs && canReorder ? { [weak self] node, gesture in + self?.openTabContextMenu(key: key, sourceNode: node, gesture: gesture) + } : nil) + } + let tabsContainerSize = CGSize(width: size.width - sideInset * 2.0, height: tabsHeight) + let tabsContainerEffectiveSize = self.tabsContainer.update( + transition: ComponentTransition(transition), + component: AnyComponent(TabSelectorComponent( + colors: TabSelectorComponent.Colors( + foreground: presentationData.theme.list.itemSecondaryTextColor, + selection: presentationData.theme.list.itemAccentColor + ), + theme: presentationData.theme, + customLayout: TabSelectorComponent.CustomLayout( + font: Font.medium(14.0), + spacing: 6.0, + fillWidth: true, + lineSelection: true + ), + items: items, + selectedId: self.currentPaneKey, + setSelectedId: { [weak self] id in + guard let strongSelf = self, let key = id.base as? PeerInfoPaneKey else { + return + } + if strongSelf.currentPaneKey == key { + if let requestExpandTabs = strongSelf.requestExpandTabs, requestExpandTabs() { + } else { + let _ = strongSelf.currentPane?.node.scrollToTop() + } + return + } + if strongSelf.currentPanes[key] != nil { + strongSelf.currentPaneKey = key + + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + + strongSelf.currentPaneUpdated?(true) + + strongSelf.currentPaneStatusPromise.set(strongSelf.currentPane?.node.status ?? .single(nil)) + strongSelf.nextPaneStatusPromise.set(.single(nil)) + strongSelf.paneTransitionPromise.set(nil) + } + } else if strongSelf.pendingSwitchToPaneKey != key { + strongSelf.pendingSwitchToPaneKey = key + strongSelf.expandOnSwitch = true + + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + } + } + }, + transitionFraction: -self.transitionFraction + )), + environment: {}, + containerSize: tabsContainerSize + ) + let tabContainerFrameOriginX = items.count == 1 ? sideInset : floorToScreenPixels((size.width - tabsContainerEffectiveSize.width) / 2.0) + let tabContainerFrame = CGRect(origin: CGPoint(x: tabContainerFrameOriginX, y: 10.0 - tabsOffset), size: tabsContainerSize) + if let tabsContainerView = self.tabsContainer.view { + if tabsContainerView.superview == nil { + self.view.insertSubview(tabsContainerView, belowSubview: self.tabsSeparatorNode.view) + } + transition.updateFrame(view: tabsContainerView, frame: tabContainerFrame) + transition.updateAlpha(layer: tabsContainerView.layer, alpha: tabsAlpha) + } + for (_, pane) in self.pendingPanes { let paneTransition: ContainedViewLayoutTransition = .immediate paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) @@ -1425,3 +1697,23 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } } } + +private final class TabsExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = false + let ignoreContentTouches: Bool = false + let blurBackground: Bool = true + + private let sourceNode: ContextExtractedContentContainingNode + + init(sourceNode: ContextExtractedContentContainingNode) { + self.sourceNode = sourceNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index e9ad5add11..5627e06ed1 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -112,6 +112,7 @@ import OldChannelsController import UrlHandling import VerifyAlertController import GiftViewScreen +import PeerMessagesMediaPlaylist public enum PeerInfoAvatarEditingMode { case generic @@ -4114,6 +4115,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.performButtonAction(key: key, gesture: gesture) } + self.headerNode.displaySavedMusic = { [weak self] in + self?.displaySavedMusic() + } + self.headerNode.cancelUpload = { [weak self] in guard let strongSelf = self else { return @@ -5007,8 +5012,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added) }, - convertToStars: { [weak profileGifts] in - guard let profileGifts, let reference = gift.reference else { + convertToStars: { [weak profileGifts] reference in + guard let profileGifts else { return } profileGifts.convertStarGift(reference: reference) @@ -5019,8 +5024,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } return profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId) }, - upgradeGift: { [weak profileGifts] formId, keepOriginalInfo in - guard let profileGifts, let reference = gift.reference else { + upgradeGift: { [weak profileGifts] formId, reference, keepOriginalInfo in + guard let profileGifts else { return .never() } return profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo) @@ -6004,6 +6009,59 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } + var previousSavedMusicTimestamp: Double? + private func displaySavedMusic() { + guard let savedMusicContext = self.data?.savedMusicContext else { + return + } + + let currentTimestamp = CACurrentMediaTime() + if let previousTimestamp = self.previousSavedMusicTimestamp, currentTimestamp < previousTimestamp + 1.0 { + return + } + self.previousSavedMusicTimestamp = currentTimestamp + + let _ = (self.context.sharedContext.mediaManager.globalMediaPlayerState + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] accountStateAndType in + guard let self else { + return + } + let peerId = self.peerId + var initialId: Int32 + if let initialFileId = self.data?.savedMusicState?.files.first?.fileId { + initialId = Int32(clamping: initialFileId.id % Int64(Int32.max)) + } else { + initialId = 0 + } + + let canReorder = peerId == self.context.account.peerId + var playlistLocation: PeerMessagesPlaylistLocation = .savedMusic(context: savedMusicContext, at: initialId, canReorder: canReorder) + + if let (account, stateOrLoading, _) = accountStateAndType, self.context.account.peerId == account.peerId, case let .state(state) = stateOrLoading, let location = state.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, _) = location, savedMusicContext.peerId == peerId { + if let itemId = state.item.id as? PeerMessagesMediaPlaylistItemId { + initialId = itemId.messageId.id + } + playlistLocation = .savedMusic(context: savedMusicContext, at: initialId, canReorder: canReorder) + } else { + self.context.sharedContext.mediaManager.setPlaylist((self.context, PeerMessagesMediaPlaylist(context: self.context, location: playlistLocation, chatLocationContextHolder: nil)), type: .music, control: .playback(.play)) + } + + Queue.mainQueue().after(0.1) { + let musicController = self.context.sharedContext.makeOverlayAudioPlayerController( + context: self.context, + chatLocation: .peer(id: peerId), + type: .music, + initialMessageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: initialId), + initialOrder: .regular, + playlistLocation: playlistLocation, + parentNavigationController: self.controller?.navigationController as? NavigationController + ) + self.controller?.present(musicController, in: .window(.root)) + } + }) + } + private func performButtonAction(key: PeerInfoHeaderButtonKey, gesture: ContextGesture?) { guard let controller = self.controller else { return @@ -11463,7 +11521,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else { updatedFilter.insert(value) } - if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limited) && !updatedFilter.contains(.unique) { + if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limitedUpgradable) && !updatedFilter.contains(.limitedNonUpgradable) && !updatedFilter.contains(.unique) { updatedFilter.insert(.unlimited) } if !updatedFilter.contains(.displayed) && !updatedFilter.contains(.hidden) { @@ -11479,7 +11537,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in var updatedFilter = filter updatedFilter.remove(.unlimited) - updatedFilter.remove(.limited) + updatedFilter.remove(.limitedUpgradable) + updatedFilter.remove(.limitedNonUpgradable) updatedFilter.remove(.unique) updatedFilter.insert(value) giftsContext?.updateFilter(updatedFilter) @@ -11501,11 +11560,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switchToFilter(.unlimited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in - return filter.contains(.limited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in - toggleFilter(.limited) + toggleFilter(.limitedNonUpgradable) }, longPressAction: { _, f in - switchToFilter(.limited) + switchToFilter(.limitedNonUpgradable) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in + return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.limitedUpgradable) + }, longPressAction: { _, f in + switchToFilter(.limitedUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil @@ -12237,7 +12303,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) + let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight)) if additive { transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame) @@ -12622,7 +12688,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -12672,7 +12738,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) { rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false)) } - if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) { + if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories), channel.linkedBotId == nil { rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0) } else if self.isMyProfile { rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0) @@ -13486,27 +13552,39 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc if let updatedPresentationData = updatedPresentationData { presentationDataSignal = updatedPresentationData.signal } else if self.peerId != self.context.account.peerId { - let themeEmoticon: Signal = self.cachedDataPromise.get() - |> map { cachedData -> String? in + let chatTheme: Signal = self.cachedDataPromise.get() + |> map { cachedData -> ChatTheme? in if let cachedData = cachedData as? CachedUserData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedData as? CachedGroupData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else if let cachedData = cachedData as? CachedChannelData { - return cachedData.themeEmoticon + return cachedData.chatTheme } else { return nil } } |> distinctUntilChanged - presentationDataSignal = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: false), themeEmoticon) - |> map { presentationData, chatThemes, themeEmoticon -> PresentationData in + presentationDataSignal = combineLatest( + queue: Queue.mainQueue(), + context.sharedContext.presentationData, + context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: false), + chatTheme + ) + |> map { presentationData, chatThemes, chatTheme -> PresentationData in var presentationData = presentationData - if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoticon == themeEmoticon }) { - if let theme = makePresentationTheme(cloudTheme: theme, dark: presentationData.theme.overallDarkAppearance) { - presentationData = presentationData.withUpdated(theme: theme) - presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + if let chatTheme { + switch chatTheme { + case let .emoticon(emoticon): + if let theme = chatThemes.first(where: { $0.emoticon == emoticon }) { + if let theme = makePresentationTheme(cloudTheme: theme, dark: presentationData.theme.overallDarkAppearance) { + presentationData = presentationData.withUpdated(theme: theme) + presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + } + } + case .gift: + break } } return presentationData @@ -14312,7 +14390,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } let headerInset = sectionInset - topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.savedMessagesPeer ?? self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, peerNotificationSettings: self.screenNode.data?.peerNotificationSettings, threadNotificationSettings: self.screenNode.data?.threadNotificationSettings, globalNotificationSettings: self.screenNode.data?.globalNotificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, profileGiftsContext: self.screenNode.data?.profileGiftsContext, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: false, animateHeader: false) + topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.savedMessagesPeer ?? self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, peerNotificationSettings: self.screenNode.data?.peerNotificationSettings, threadNotificationSettings: self.screenNode.data?.threadNotificationSettings, globalNotificationSettings: self.screenNode.data?.globalNotificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, profileGiftsContext: self.screenNode.data?.profileGiftsContext, screenData: self.screenNode.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: false, animateHeader: false) } let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index 44ce59bb97..ac6dc07ef7 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -385,7 +385,7 @@ final class PeerInfoStoryGridScreenComponent: Component { } if let selectionModeCompletion = component.selectionModeCompletion { - selectionModeCompletion(Array(paneNode.selectedItems.values)) + selectionModeCompletion(Array(paneNode.selectedItems.sorted(by: { $0.key < $1.key }).map(\.value))) environment.controller()?.dismiss() return } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 1337347ae8..26e09bea73 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -349,7 +349,7 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { } else { updatedFilter.insert(value) } - if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limited) && !updatedFilter.contains(.unique) { + if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limitedUpgradable) && !updatedFilter.contains(.limitedNonUpgradable) && !updatedFilter.contains(.unique) { updatedFilter.insert(.unlimited) } if !updatedFilter.contains(.displayed) && !updatedFilter.contains(.hidden) { @@ -365,7 +365,8 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in var updatedFilter = filter updatedFilter.remove(.unlimited) - updatedFilter.remove(.limited) + updatedFilter.remove(.limitedUpgradable) + updatedFilter.remove(.limitedNonUpgradable) updatedFilter.remove(.unique) updatedFilter.insert(value) giftsContext?.updateFilter(updatedFilter) @@ -387,11 +388,18 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { switchToFilter(.unlimited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in - return filter.contains(.limited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in - toggleFilter(.limited) + toggleFilter(.limitedNonUpgradable) }, longPressAction: { _, f in - switchToFilter(.limited) + switchToFilter(.limitedNonUpgradable) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in + return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.limitedUpgradable) + }, longPressAction: { _, f in + switchToFilter(.limitedUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift index 7c1e42f704..3ec904d2a4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftContextPreviewController.swift @@ -7,6 +7,7 @@ import SwiftSignalKit import ViewControllerComponent import ComponentDisplayAdapters import TelegramPresentationData +import TelegramStringFormatting import AccountContext import TelegramCore import MultilineTextComponent @@ -68,7 +69,7 @@ private final class GiftContextPreviewComponent: Component { case let .generic(gift): subject = .generic(gift.file) case let .unique(gift): - subject = .unique(gift) + subject = .unique(nil, gift) } let animationSize = self.animation.update( @@ -81,7 +82,7 @@ private final class GiftContextPreviewComponent: Component { animationScale: nil, displayAnimationStars: false, externalState: self.giftCompositionExternalState, - requestUpdate: { [weak state] in + requestUpdate: { [weak state] _ in state?.updated() } )), @@ -120,7 +121,7 @@ private final class GiftContextPreviewComponent: Component { let subtitleSize = self.subtitle.update( transition: .immediate, component: AnyComponent(MultilineTextComponent(text: .plain( - NSAttributedString(string: "\(environment.strings.Gift_Unique_Collectible) #\(presentationStringsFormattedNumber(uniqueGift.number, environment.dateTimeFormat.groupingSeparator))", font: Font.regular(13.0), textColor: vibrantColor) + NSAttributedString(string: "\(environment.strings.Gift_Unique_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))", font: Font.regular(13.0), textColor: vibrantColor) ))), environment: {}, containerSize: availableSize diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift index e1d4f54e32..4412a7ea4a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/GiftsListView.swift @@ -611,8 +611,8 @@ final class GiftsListView: UIView { } self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added) }, - convertToStars: { [weak self] in - guard let self, let reference = product.reference else { + convertToStars: { [weak self] reference in + guard let self else { return } self.profileGifts.convertStarGift(reference: reference) @@ -623,8 +623,8 @@ final class GiftsListView: UIView { } return self.profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId) }, - upgradeGift: { [weak self] formId, keepOriginalInfo in - guard let self, let reference = product.reference else { + upgradeGift: { [weak self] formId, reference, keepOriginalInfo in + guard let self else { return .never() } return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo) @@ -656,7 +656,7 @@ final class GiftsListView: UIView { var title = "" if case let .unique(uniqueGift) = product.gift { - title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, params.presentationData.dateTimeFormat.groupingSeparator))" + title = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: params.presentationData.dateTimeFormat))" } if pinnedToTop { @@ -996,7 +996,7 @@ final class GiftsListView: UIView { fadeTransition.setAlpha(view: self.emptyResultsClippingView, alpha: visibleHeight < 300.0 ? 0.0 : 1.0) - if self.peerId == self.context.account.peerId, !self.canSelect && !self.filteredResultsAreEmpty && self.profileGifts.collectionId == nil { + if self.peerId == self.context.account.peerId, !self.canSelect && !self.filteredResultsAreEmpty && self.profileGifts.collectionId == nil && self.emptyResultsClippingView.isHidden { let footerText: ComponentView if let current = self.footerText { footerText = current @@ -1024,6 +1024,13 @@ final class GiftsListView: UIView { transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: floor((size.width - footerTextSize.width) / 2.0), y: contentHeight), size: footerTextSize)) } contentHeight += footerTextSize.height + } else if let footerText = self.footerText { + self.footerText = nil + if let view = footerText.view { + fadeTransition.setAlpha(view: view, alpha: 0.0, completion: { _ in + view.removeFromSuperview() + }) + } } return contentHeight diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 5d3442cf5f..35ebc2693d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -90,7 +90,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr private let tabSelector = ComponentView() public private(set) var currentCollection: GiftCollection = .all - private var footerText: ComponentView? private var panelBackground: NavigationBackgroundNode? private var panelSeparator: ASDisplayNode? private var panelButton: ComponentView? @@ -383,7 +382,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr var replacingTitle = "" for gift in pinnedGifts { if gift.reference == unpinnedReference, case let .unique(uniqueGift) = gift.gift { - replacingTitle = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, presentationData.dateTimeFormat.groupingSeparator))" + replacingTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: presentationData.dateTimeFormat))" } } @@ -395,7 +394,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr var title = "" if case let .unique(uniqueGift) = gift.gift { - title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, presentationData.dateTimeFormat.groupingSeparator))" + title = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: presentationData.dateTimeFormat))" } let _ = self.scrollToTop() @@ -1058,7 +1057,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr case let .generic(gift): giftFile = gift.file case let .unique(uniqueGift): - giftTitle = uniqueGift.title + " #\(presentationStringsFormattedNumber(uniqueGift.number, currentParams.presentationData.dateTimeFormat.groupingSeparator))" + giftTitle = uniqueGift.title + " #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: currentParams.presentationData.dateTimeFormat))" for attribute in uniqueGift.attributes { if case let .model(_, file, _) = attribute { giftFile = file @@ -1126,7 +1125,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } else { var title = "" if case let .unique(uniqueGift) = gift.gift { - title = "\(uniqueGift.title) #\(presentationStringsFormattedNumber(uniqueGift.number, presentationData.dateTimeFormat.groupingSeparator))" + title = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: presentationData.dateTimeFormat))" } toastTitle = strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string toastText = strings.PeerInfo_Gifts_ToastPinned_Text @@ -1378,7 +1377,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr case let .generic(gift): giftFile = gift.file case let .unique(uniqueGift): - giftTitle = uniqueGift.title + " #\(presentationStringsFormattedNumber(uniqueGift.number, currentParams.presentationData.dateTimeFormat.groupingSeparator))" + giftTitle = uniqueGift.title + " #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: currentParams.presentationData.dateTimeFormat))" for attribute in uniqueGift.attributes { if case let .model(_, file, _) = attribute { giftFile = file diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift index b5feaba4b9..fd2a593c0f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelInfoScreen.swift @@ -17,6 +17,7 @@ import PlainButtonComponent import Markdown import PremiumUI import LottieComponent +import AnimatedTextComponent private final class ProfileLevelInfoScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -376,10 +377,38 @@ private final class ProfileLevelInfoScreenComponent: Component { descriptionTextString = environment.strings.ProfileLevelInfo_OtherDescription(component.peer.compactDisplayTitle).string } + //TODO:localize + var titleItems: [AnimatedTextComponent.Item] = [] + if self.isPreviewingPendingRating { + titleItems.append(AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: false, + content: .text("Future ") + )) + titleItems.append(AnimatedTextComponent.Item( + id: AnyHashable(1), + isUnbreakable: true, + content: .text("Rating") + )) + } else { + titleItems.append(AnimatedTextComponent.Item( + id: AnyHashable(1), + isUnbreakable: true, + content: .text("Rating") + )) + } + + let _ = titleString let titleSize = self.title.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleString, font: Font.semibold(17.0), textColor: environment.theme.list.itemPrimaryTextColor)) + transition: transition, + component: AnyComponent(AnimatedTextComponent( + font: Font.semibold(17.0), + color: environment.theme.list.itemPrimaryTextColor, + items: titleItems, + noDelay: true, + animateScale: false, + preferredDirectionIsDown: true, + blur: true )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) @@ -389,7 +418,7 @@ private final class ProfileLevelInfoScreenComponent: Component { if titleView.superview == nil { self.navigationBarContainer.addSubview(titleView) } - titleView.frame = titleFrame + transition.setFrame(view: titleView, frame: titleFrame) } contentHeight += 56.0 @@ -433,47 +462,25 @@ private final class ProfileLevelInfoScreenComponent: Component { if let nextLevelStars = component.starRating.nextLevelStars { badgeTextSuffix = " / \(starCountString(Int64(nextLevelStars), decimalSeparator: "."))" } - if let nextLevelStars = component.starRating.nextLevelStars { + if component.starRating.stars < 0 { + levelFraction = 0.5 + } else if let nextLevelStars = component.starRating.nextLevelStars { levelFraction = Double(component.starRating.stars - component.starRating.currentLevelStars) / Double(nextLevelStars - component.starRating.currentLevelStars) - } else if component.starRating.stars > 0 { - levelFraction = 1.0 } else { - levelFraction = 0.0 + levelFraction = 1.0 } } levelFraction = max(0.0, levelFraction) - - /*let levelInfoSize = self.levelInfo.update( - transition: .immediate, - component: AnyComponent(PremiumLimitDisplayComponent( - inactiveColor: environment.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5), - activeColors: gradientColors, - inactiveTitle: environment.strings.ProfileLevelInfo_LevelIndex(Int32(currentLevel)), - inactiveValue: "", - inactiveTitleColor: environment.theme.list.itemPrimaryTextColor, - activeTitle: "", - activeValue: nextLevel.flatMap { environment.strings.ProfileLevelInfo_LevelIndex(Int32($0)) } ?? "", - activeTitleColor: .white, - badgeIconName: "Peer Info/ProfileLevelProgressIcon", - badgeText: badgeText, - badgeTextSuffix: badgeTextSuffix, - badgePosition: levelFraction, - badgeGraphPosition: levelFraction, - invertProgress: true, - isPremiumDisabled: false - )), - environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 200.0) - )*/ - let _ = levelFraction + + //TODO:localize let levelInfoSize = self.levelInfo.update( transition: isChangingPreview ? ComponentTransition.immediate.withUserData(ProfileLevelRatingBarComponent.TransitionHint(animate: true)) : .immediate, component: AnyComponent(ProfileLevelRatingBarComponent( theme: environment.theme, value: levelFraction, - leftLabel: environment.strings.ProfileLevelInfo_LevelIndex(Int32(currentLevel)), - rightLabel: nextLevel.flatMap { environment.strings.ProfileLevelInfo_LevelIndex(Int32($0)) } ?? "", + leftLabel: currentLevel < 0 ? "" : environment.strings.ProfileLevelInfo_LevelIndex(Int32(currentLevel)), + rightLabel: currentLevel < 0 ? "Negative rating" : nextLevel.flatMap { environment.strings.ProfileLevelInfo_LevelIndex(Int32($0)) } ?? "", badgeValue: badgeText, badgeTotal: badgeTextSuffix, level: Int(currentLevel) @@ -491,12 +498,21 @@ private final class ProfileLevelInfoScreenComponent: Component { contentHeight += 129.0 if let secondaryDescriptionTextString { + let changingPreviewAnimationOffset: CGFloat = self.isPreviewingPendingRating ? -100.0 : 100.0 + let transitionBlurRadius: CGFloat = 10.0 + if isChangingPreview, let secondaryDescriptionTextView = self.secondaryDescriptionText?.view { self.secondaryDescriptionText = nil - transition.setTransform(view: secondaryDescriptionTextView, transform: CATransform3DMakeScale(0.9, 0.9, 1.0)) + transition.setTransform(view: secondaryDescriptionTextView, transform: CATransform3DMakeTranslation(changingPreviewAnimationOffset, 0.0, 0.0)) alphaTransition.setAlpha(view: secondaryDescriptionTextView, alpha: 0.0, completion: { [weak secondaryDescriptionTextView] _ in secondaryDescriptionTextView?.removeFromSuperview() }) + + if let blurFilter = CALayer.blur() { + blurFilter.setValue(transitionBlurRadius as NSNumber, forKey: "inputRadius") + secondaryDescriptionTextView.layer.filters = [blurFilter] + secondaryDescriptionTextView.layer.animate(from: 0.0 as NSNumber, to: transitionBlurRadius as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.3, removeOnCompletion: false) + } } contentHeight -= 8.0 @@ -510,9 +526,16 @@ private final class ProfileLevelInfoScreenComponent: Component { self.secondaryDescriptionText = secondaryDescriptionText } + let secondaryTextColor: UIColor + if currentLevel < 0 { + secondaryTextColor = UIColor(rgb: 0xFF3B30) + } else { + secondaryTextColor = environment.theme.list.itemSecondaryTextColor + } + let secondaryDescriptionAttributedString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(secondaryDescriptionTextString, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor), + body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: secondaryTextColor), + bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: secondaryTextColor), link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor), linkAttribute: { url in return ("URL", url) @@ -563,8 +586,16 @@ private final class ProfileLevelInfoScreenComponent: Component { if secondaryDescriptionTextView.superview == nil { self.scrollContentView.addSubview(secondaryDescriptionTextView) if isChangingPreview { - transition.animateScale(view: secondaryDescriptionTextView, from: 0.9, to: 1.0) + transition.animatePosition(view: secondaryDescriptionTextView, from: CGPoint(x: -changingPreviewAnimationOffset, y: 0.0), to: CGPoint(), additive: true) alphaTransition.animateAlpha(view: secondaryDescriptionTextView, from: 0.0, to: 1.0) + + if let blurFilter = CALayer.blur() { + blurFilter.setValue(transitionBlurRadius as NSNumber, forKey: "inputRadius") + secondaryDescriptionTextView.layer.filters = [blurFilter] + secondaryDescriptionTextView.layer.animate(from: transitionBlurRadius as NSNumber, to: 0.0 as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.3, removeOnCompletion: false, completion: { [weak secondaryDescriptionTextView] _ in + secondaryDescriptionTextView?.layer.filters = nil + }) + } } } secondaryDescriptionTextTransition.setPosition(view: secondaryDescriptionTextView, position: secondaryDescriptionTextFrame.center) diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarBadge.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarBadge.swift index 80fb328174..c79a7af616 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarBadge.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarBadge.swift @@ -47,11 +47,12 @@ final class ProfileLevelRatingBarBadge: Component { final class View: UIView { private let badgeView: UIView private let badgeMaskView: UIView - private let badgeShapeLayer = SimpleShapeLayer() + private let badgeShapeView: UIImageView private let badgeShapeAnimation = ComponentView() private let badgeForeground: SimpleLayer - let badgeIcon: UIImageView + private let badgeIcon: UIImageView + private var disappearingBadgeIcon: UIImageView? private let badgeLabel = ComponentView() private let suffixLabel = ComponentView() @@ -68,11 +69,10 @@ final class ProfileLevelRatingBarBadge: Component { self.badgeView.alpha = 0.0 self.badgeView.layer.anchorPoint = CGPoint() - self.badgeShapeLayer.fillColor = UIColor.white.cgColor - self.badgeShapeLayer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) + self.badgeShapeView = UIImageView() self.badgeMaskView = UIView() - self.badgeMaskView.layer.addSublayer(self.badgeShapeLayer) + self.badgeMaskView.addSubview(self.badgeShapeView) self.badgeView.mask = self.badgeMaskView self.badgeForeground = SimpleLayer() @@ -108,17 +108,49 @@ final class ProfileLevelRatingBarBadge: Component { self.isUpdating = false } - if self.component == nil { - self.badgeIcon.image = UIImage(bundleImageName: "Peer Info/ProfileLevelProgressIcon")?.withRenderingMode(.alwaysTemplate) + var labelsTransition = transition + if let hint = transition.userData(TransitionHint.self), hint.animateText { + labelsTransition = .spring(duration: 0.5) + } + + let previousComponent = self.component + if previousComponent == nil || (previousComponent?.title == "") != (component.title == "") { + if !labelsTransition.animation.isImmediate, self.badgeIcon.image != nil { + if let disappearingBadgeIcon = self.disappearingBadgeIcon { + self.disappearingBadgeIcon = nil + disappearingBadgeIcon.removeFromSuperview() + } + let disappearingBadgeIcon = UIImageView() + disappearingBadgeIcon.contentMode = self.badgeIcon.contentMode + disappearingBadgeIcon.frame = self.badgeIcon.frame + disappearingBadgeIcon.image = self.badgeIcon.image + disappearingBadgeIcon.tintColor = self.badgeIcon.tintColor + + self.badgeView.insertSubview(disappearingBadgeIcon, aboveSubview: self.badgeIcon) + labelsTransition.setScale(view: disappearingBadgeIcon, scale: 0.001) + labelsTransition.setAlpha(view: disappearingBadgeIcon, alpha: 0.0, completion: { [weak self, weak disappearingBadgeIcon] _ in + guard let self, let disappearingBadgeIcon else { + return + } + disappearingBadgeIcon.removeFromSuperview() + if self.disappearingBadgeIcon === disappearingBadgeIcon { + self.disappearingBadgeIcon = nil + } + }) + labelsTransition.animateScale(view: self.badgeIcon, from: 0.001, to: 1.0) + labelsTransition.animateAlpha(view: self.badgeIcon, from: 0.0, to: 1.0) + } + + if component.title.isEmpty { + self.badgeIcon.image = UIImage(bundleImageName: "Peer Info/ProfileLevelWarningIcon")?.withRenderingMode(.alwaysTemplate) + } else { + self.badgeIcon.image = UIImage(bundleImageName: "Peer Info/ProfileLevelProgressIcon")?.withRenderingMode(.alwaysTemplate) + } } self.component = component self.badgeIcon.tintColor = component.theme.list.itemCheckColors.foregroundColor - - var labelsTransition = transition - if let hint = transition.userData(TransitionHint.self), hint.animateText { - labelsTransition = .spring(duration: 0.4) - } + self.disappearingBadgeIcon?.tintColor = component.theme.list.itemCheckColors.foregroundColor let badgeLabelSize = self.badgeLabel.update( transition: labelsTransition, @@ -150,7 +182,12 @@ final class ProfileLevelRatingBarBadge: Component { containerSize: CGSize(width: 300.0, height: 100.0) ) - var badgeWidth: CGFloat = badgeLabelSize.width + 3.0 + 60.0 + var badgeWidth: CGFloat = 0.0 + if !component.title.isEmpty { + badgeWidth += badgeLabelSize.width + 3.0 + 60.0 + } else { + badgeWidth += 78.0 + } if component.suffix != nil { badgeWidth += badgeSuffixSize.width + badgeSuffixSpacing } @@ -158,13 +195,21 @@ final class ProfileLevelRatingBarBadge: Component { let badgeSize = CGSize(width: badgeWidth, height: 48.0) let badgeFullSize = CGSize(width: badgeWidth, height: 48.0 + 12.0) self.badgeMaskView.frame = CGRect(origin: .zero, size: badgeFullSize) - self.badgeShapeLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -4.0), size: badgeFullSize) self.badgeView.bounds = CGRect(origin: .zero, size: badgeFullSize) self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 600.0, height: badgeFullSize.height + 10.0)) - self.badgeIcon.frame = CGRect(x: 13.0, y: 8.0, width: 30.0, height: 30.0) + let badgeIconFrame: CGRect + if !component.title.isEmpty { + badgeIconFrame = CGRect(x: 13.0, y: 8.0, width: 30.0, height: 30.0) + } else { + badgeIconFrame = CGRect(x: floor((badgeWidth - 30.0) * 0.5), y: 8.0, width: 30.0, height: 30.0) + } + labelsTransition.setFrame(view: self.badgeIcon, frame: badgeIconFrame) + if let disappearingBadgeIcon = self.disappearingBadgeIcon { + labelsTransition.setFrame(view: disappearingBadgeIcon, frame: badgeIconFrame) + } self.badgeView.alpha = 1.0 @@ -194,77 +239,63 @@ final class ProfileLevelRatingBarBadge: Component { if self.previousAvailableSize != availableSize { self.previousAvailableSize = availableSize - - let activeColors: [UIColor] = [ - component.theme.list.itemCheckColors.fillColor, - component.theme.list.itemCheckColors.fillColor - ] - - var locations: [CGFloat] = [] - let delta = 1.0 / CGFloat(activeColors.count - 1) - for i in 0 ..< activeColors.count { - locations.append(delta * CGFloat(i)) - } - - let gradient = generateGradientImage(size: CGSize(width: 200.0, height: 60.0), colors: activeColors, locations: locations, direction: .horizontal) - self.badgeForeground.contentsGravity = .resizeAspectFill - self.badgeForeground.contents = gradient?.cgImage - - self.setupGradientAnimations() } return size } - func adjustTail(size: CGSize, overflowWidth: CGFloat, transition: ComponentTransition) { - guard let component else { - return + func updateColors(background: UIColor) { + self.badgeForeground.backgroundColor = background.cgColor + } + + func adjustTail(size: CGSize, tailOffset: CGFloat, transition: ComponentTransition) { + if self.badgeShapeView.image == nil { + self.badgeShapeView.image = generateStretchableFilledCircleImage(diameter: 48.0, color: UIColor.white) } - let _ = component + self.badgeShapeView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: 48.0)) - var tailPosition = size.width * 0.5 - tailPosition += overflowWidth - tailPosition = max(36.0, min(size.width - 36.0, tailPosition)) - - let tailPositionFraction = tailPosition / size.width - transition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: size, tailPosition: tailPositionFraction, transformTail: false).cgPath) - - let badgeShapeSize = CGSize(width: 128, height: 128) + let badgeShapeSize = CGSize(width: 78, height: 60) let _ = self.badgeShapeAnimation.update( transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: "badge_with_tail"), - color: .red,//component.theme.list.itemCheckColors.fillColor, + color: .white, placeholderColor: nil, startingPosition: .begin, size: badgeShapeSize, - renderingScale: nil, + renderingScale: UIScreenScale, loop: false, playOnce: nil )), environment: {}, containerSize: badgeShapeSize ) - if let badgeShapeAnimationView = self.badgeShapeAnimation.view as? LottieComponent.View, !"".isEmpty { + if let badgeShapeAnimationView = self.badgeShapeAnimation.view as? LottieComponent.View { if badgeShapeAnimationView.superview == nil { badgeShapeAnimationView.layer.anchorPoint = CGPoint() - self.addSubview(badgeShapeAnimationView) + self.badgeMaskView.addSubview(badgeShapeAnimationView) } - let transition: ComponentTransition = .immediate + var shapeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: badgeShapeSize) + + let badgeShapeWidth = badgeShapeSize.width + + let midFrame = 359 / 2 + if tailOffset < badgeShapeWidth * 0.5 { + let frameIndex = Int(floor(CGFloat(midFrame) * tailOffset / (badgeShapeWidth * 0.5))) + badgeShapeAnimationView.setFrameIndex(index: frameIndex) + } else if tailOffset >= size.width - badgeShapeWidth * 0.5 { + let endOffset = tailOffset - (size.width - badgeShapeWidth * 0.5) + let frameIndex = midFrame + Int(floor(CGFloat(359 - midFrame) * endOffset / (badgeShapeWidth * 0.5))) + badgeShapeAnimationView.setFrameIndex(index: frameIndex) + shapeFrame.origin.x = size.width - badgeShapeWidth + } else { + badgeShapeAnimationView.setFrameIndex(index: midFrame) + shapeFrame.origin.x = tailOffset - badgeShapeWidth * 0.5 + } - let shapeFrame = CGRect(origin: CGPoint(x: 0.0, y: -10.0), size: badgeShapeSize) badgeShapeAnimationView.center = shapeFrame.origin badgeShapeAnimationView.bounds = CGRect(origin: CGPoint(), size: shapeFrame.size) - - let scaleFactor: CGFloat = 144.0 / (946.0 / 4.0) - transition.setScale(view: badgeShapeAnimationView, scale: scaleFactor) - - let badgeShapeWidth = floor(shapeFrame.width * scaleFactor) - let badgeShapeOffset = -overflowWidth / badgeShapeWidth - let _ = badgeShapeOffset - - //badgeShapeAnimationView.setFrameIndex(index: 0) } //let transition: ContainedViewLayoutTransition = .immediate @@ -275,38 +306,6 @@ final class ProfileLevelRatingBarBadge: Component { let transition: ContainedViewLayoutTransition = .immediate transition.updateTransformRotation(view: self.badgeView, angle: angle) } - - private func setupGradientAnimations() { - guard let _ = self.component else { - return - } - if let _ = self.badgeForeground.animation(forKey: "movement") { - } else { - CATransaction.begin() - - let badgePreviousValue = self.badgeForeground.position.x - let badgeNewValue: CGFloat - if self.badgeForeground.position.x == -300.0 { - badgeNewValue = 0.0 - } else { - badgeNewValue = -300.0 - } - self.badgeForeground.position = CGPoint(x: badgeNewValue, y: 0.0) - - let badgeAnimation = CABasicAnimation(keyPath: "position.x") - badgeAnimation.duration = 4.5 - badgeAnimation.fromValue = badgePreviousValue - badgeAnimation.toValue = badgeNewValue - badgeAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - - CATransaction.setCompletionBlock { [weak self] in - self?.setupGradientAnimations() - } - self.badgeForeground.add(badgeAnimation, forKey: "movement") - - CATransaction.commit() - } - } } func makeView() -> View { diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarComponent.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarComponent.swift index 42c7b1931b..ce10d04682 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelInfoScreen/Sources/ProfileLevelRatingBarComponent.swift @@ -6,7 +6,6 @@ import ComponentFlow import MultilineTextComponent import BundleIconComponent import HierarchyTrackingLayer -import AnimatedTextComponent final class ProfileLevelRatingBarComponent: Component { final class TransitionHint { @@ -69,20 +68,33 @@ final class ProfileLevelRatingBarComponent: Component { } private final class AnimationState { + enum Wraparound { + case left + case right + } + + let fromLevel: Int + let toLevel: Int + let fromLeftLabelText: String + let fromRightLabelText: String let fromValue: CGFloat let toValue: CGFloat let fromBadgeSize: CGSize let startTime: Double let duration: Double - let isWraparound: Bool + let wraparound: Wraparound? - init(fromValue: CGFloat, toValue: CGFloat, fromBadgeSize: CGSize, startTime: Double, duration: Double, isWraparound: Bool) { + init(fromLevel: Int, toLevel: Int, fromLeftLabelText: String, fromRightLabelText: String, fromValue: CGFloat, toValue: CGFloat, fromBadgeSize: CGSize, startTime: Double, duration: Double, wraparound: Wraparound?) { + self.fromLevel = fromLevel + self.toLevel = toLevel + self.fromLeftLabelText = fromLeftLabelText + self.fromRightLabelText = fromRightLabelText self.fromValue = fromValue self.toValue = toValue self.fromBadgeSize = fromBadgeSize self.startTime = startTime self.duration = duration - self.isWraparound = isWraparound + self.wraparound = wraparound } func timeFraction(at timestamp: Double) -> CGFloat { @@ -91,8 +103,30 @@ final class ProfileLevelRatingBarComponent: Component { return fraction } + func stepFraction(at timestamp: Double) -> (step: Int, fraction: CGFloat) { + if self.wraparound != nil { + var t = self.timeFraction(at: timestamp) + t = bezierPoint(0.6, 0.0, 0.4, 1.0, t) + if t < 0.5 { + let vt = t / 0.5 + return (0, vt) + } else { + let vt = (t - 0.5) / 0.5 + return (1, vt) + } + } else { + let t = self.timeFraction(at: timestamp) + return (0, listViewAnimationCurveSystem(t)) + } + } + func fraction(at timestamp: Double) -> CGFloat { - return listViewAnimationCurveSystem(self.timeFraction(at: timestamp)) + let t = self.timeFraction(at: timestamp) + if self.wraparound != nil { + return listViewAnimationCurveEaseInOut(t) + } else { + return listViewAnimationCurveSystem(t) + } } func value(at timestamp: Double) -> CGFloat { @@ -100,14 +134,12 @@ final class ProfileLevelRatingBarComponent: Component { return (1.0 - fraction) * self.fromValue + fraction * self.toValue } - func wrapAroundValue(at timestamp: Double, topValue: CGFloat) -> CGFloat { - let fraction = self.fraction(at: timestamp) - if fraction <= 0.5 { - let halfFraction = fraction / 0.5 - return (1.0 - halfFraction) * self.fromValue + halfFraction * topValue + func wrapAroundValue(at timestamp: Double, bottomValue: CGFloat, topValue: CGFloat) -> CGFloat { + let (step, fraction) = self.stepFraction(at: timestamp) + if step == 0 { + return (1.0 - fraction) * self.fromValue + fraction * topValue } else { - let halfFraction = (fraction - 0.5) / 0.5 - return halfFraction * self.toValue + return (1.0 - fraction) * bottomValue + fraction * self.toValue } } @@ -123,6 +155,7 @@ final class ProfileLevelRatingBarComponent: Component { final class View: UIView { private let barBackground: UIImageView private let backgroundClippingContainer: UIView + private let foregroundBarClippingContainer: UIView private let foregroundClippingContainer: UIView private let barForeground: UIImageView @@ -139,13 +172,29 @@ final class ProfileLevelRatingBarComponent: Component { private var hierarchyTracker: HierarchyTrackingLayer? private var animationLink: SharedDisplayLinkDriver.Link? + private var badgePhysicsLink: SharedDisplayLinkDriver.Link? private var animationState: AnimationState? + private var previousAnimationTimestamp: Double? + private var previousAnimationTimeFraction: CGFloat? + private var animationDeltaTime: Double? + private var animationIsMovingOverStep: Bool = false + + private var badgeAngularSpeed: CGFloat = 0.0 + private var badgeScale: CGFloat = 1.0 + private var badgeAngle: CGFloat = 0.0 + private var previousPhysicsTimestamp: Double? + + private var testFraction: CGFloat? + private var startTestFraction: CGFloat? + override init(frame: CGRect) { self.barBackground = UIImageView() self.backgroundClippingContainer = UIView() self.backgroundClippingContainer.clipsToBounds = true + self.foregroundBarClippingContainer = UIView() + self.foregroundBarClippingContainer.clipsToBounds = true self.foregroundClippingContainer = UIView() self.foregroundClippingContainer.clipsToBounds = true self.barForeground = UIImageView() @@ -161,7 +210,28 @@ final class ProfileLevelRatingBarComponent: Component { return } self.updateAnimations() + + if value { + if self.badgePhysicsLink == nil { + let badgePhysicsLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] _ in + guard let self else { + return + } + self.updateBadgePhysics() + }) + self.badgePhysicsLink = badgePhysicsLink + } + } else { + if let badgePhysicsLink = self.badgePhysicsLink { + self.badgePhysicsLink = nil + badgePhysicsLink.invalidate() + } + } } + + #if DEBUG + self.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.onPanGesture(_:)))) + #endif } required init?(coder: NSCoder) { @@ -171,7 +241,40 @@ final class ProfileLevelRatingBarComponent: Component { deinit { } + @objc private func onPanGesture(_ recognizer: UIPanGestureRecognizer) { + switch recognizer.state { + case .began, .changed: + if self.testFraction == nil { + self.testFraction = self.component?.value + } + if self.startTestFraction == nil { + if let testFraction = self.testFraction { + self.startTestFraction = testFraction + } + } + if let startTestFraction = self.startTestFraction { + let x = recognizer.translation(in: self).x + var value: CGFloat = startTestFraction + x / self.bounds.width + value = max(0.0, min(1.0, value)) + self.testFraction = value + self.state?.updated(transition: .immediate, isLocal: true) + } + case .ended, .cancelled: + self.startTestFraction = nil + default: + break + } + } + private func updateAnimations() { + let timestamp = CACurrentMediaTime() + let deltaTime: CGFloat + if let previousAnimationTimestamp = self.previousAnimationTimestamp { + deltaTime = min(0.2, timestamp - previousAnimationTimestamp) + } else { + deltaTime = 1.0 / 60.0 + } + if let hierarchyTracker = self.hierarchyTracker, hierarchyTracker.isInHierarchy { if self.animationState != nil { if self.animationLink == nil { @@ -194,15 +297,104 @@ final class ProfileLevelRatingBarComponent: Component { } if let animationState = self.animationState { - if animationState.timeFraction(at: CACurrentMediaTime()) >= 1.0 { + let timeFraction = animationState.timeFraction(at: timestamp) + if timeFraction >= 1.0 { self.animationState = nil self.updateAnimations() + return + } else { + if let previousAnimationTimeFraction = self.previousAnimationTimeFraction { + if previousAnimationTimeFraction < 0.5 && timeFraction >= 0.5 { + self.animationIsMovingOverStep = true + } + } + self.previousAnimationTimeFraction = timeFraction } + } else { + self.previousAnimationTimeFraction = nil } + self.animationDeltaTime = Double(deltaTime) + if self.animationState != nil && !self.isUpdating { self.state?.updated(transition: .immediate, isLocal: true) } + + self.animationDeltaTime = nil + self.animationIsMovingOverStep = false + } + + private func addBadgeDeltaX(value: CGFloat, deltaTime: CGFloat) { + var deltaTime = deltaTime + deltaTime /= UIView.animationDurationFactor() + let horizontalVelocity = value / deltaTime + var badgeAngle = self.badgeAngle + badgeAngle -= horizontalVelocity * 0.00005 + let maxAngle: CGFloat = 0.1 + if abs(badgeAngle) > maxAngle { + badgeAngle = badgeAngle < 0.0 ? -maxAngle : maxAngle + } + self.badgeAngle = badgeAngle + } + + private func updateBadgePhysics() { + let timestamp = CACurrentMediaTime() + + var deltaTime: CGFloat + if let previousPhysicsTimestamp = self.previousPhysicsTimestamp { + deltaTime = CGFloat(min(1.0 / 60.0, timestamp - previousPhysicsTimestamp)) + } else { + deltaTime = CGFloat(1.0 / 60.0) + } + self.previousPhysicsTimestamp = timestamp + deltaTime /= UIView.animationDurationFactor() + + let testSpringFriction: CGFloat = 18.5 + let testSpringConstant: CGFloat = 243.0 + + let frictionConstant: CGFloat = testSpringFriction + let springConstant: CGFloat = testSpringConstant + let time: CGFloat = deltaTime + + var badgeAngle = self.badgeAngle + + // friction force = velocity * friction constant + let frictionForce = self.badgeAngularSpeed * frictionConstant + // spring force = (target point - current position) * spring constant + let springForce = -badgeAngle * springConstant + // force = spring force - friction force + let force = springForce - frictionForce + + // velocity = current velocity + force * time / mass + self.badgeAngularSpeed = self.badgeAngularSpeed + force * time + // position = current position + velocity * time + badgeAngle = badgeAngle + self.badgeAngularSpeed * time + badgeAngle = badgeAngle.isNaN ? 0.0 : badgeAngle + + let epsilon: CGFloat = 0.01 + if abs(badgeAngle) < epsilon && abs(self.badgeAngularSpeed) < epsilon { + badgeAngle = 0.0 + self.badgeAngularSpeed = 0.0 + } + + if abs(badgeAngle) > 0.22 { + badgeAngle = badgeAngle < 0.0 ? -0.22 : 0.22 + } + + if self.badgeAngle != badgeAngle { + self.badgeAngle = badgeAngle + self.updateBadgeTransform() + } + } + + private func updateBadgeTransform() { + guard let badgeView = self.badge.view else { + return + } + var transform = CATransform3DIdentity + transform = CATransform3DScale(transform, self.badgeScale, self.badgeScale, 1.0) + transform = CATransform3DRotate(transform, self.badgeAngle, 0.0, 0.0, 1.0) + badgeView.layer.transform = transform } func update(component: ProfileLevelRatingBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { @@ -215,11 +407,31 @@ final class ProfileLevelRatingBarComponent: Component { var labelsTransition = transition if let previousComponent = self.component, let hint = transition.userData(TransitionHint.self), hint.animate { - labelsTransition = .spring(duration: 0.4) + labelsTransition = .spring(duration: 0.5) + var fromLevel = previousComponent.level + var fromLeftLabelText = previousComponent.leftLabel + var fromRightLabelText = previousComponent.rightLabel + let toLevel: Int = component.level let fromValue: CGFloat if let animationState = self.animationState { - fromValue = animationState.value(at: CACurrentMediaTime()) + if let wraparound = animationState.wraparound { + let wraparoundEnd: CGFloat + switch wraparound { + case .left: + wraparoundEnd = 0.0 + case .right: + wraparoundEnd = 1.0 + } + if animationState.stepFraction(at: CACurrentMediaTime()).step == 0 { + fromLevel = animationState.fromLevel + fromLeftLabelText = animationState.fromLeftLabelText + fromRightLabelText = animationState.fromRightLabelText + } + fromValue = animationState.wrapAroundValue(at: CACurrentMediaTime(), bottomValue: 1.0 - wraparoundEnd, topValue: wraparoundEnd) + } else { + fromValue = animationState.value(at: CACurrentMediaTime()) + } } else { fromValue = previousComponent.value } @@ -229,13 +441,23 @@ final class ProfileLevelRatingBarComponent: Component { } else { fromBadgeSize = CGSize() } + var wraparound: AnimationState.Wraparound? + var duration = 0.4 + if previousComponent.level != component.level { + wraparound = component.level > previousComponent.level ? .right : .left + duration = 0.8 + } self.animationState = AnimationState( + fromLevel: fromLevel, + toLevel: toLevel, + fromLeftLabelText: fromLeftLabelText, + fromRightLabelText: fromRightLabelText, fromValue: fromValue, toValue: component.value, fromBadgeSize: fromBadgeSize, startTime: CACurrentMediaTime(), - duration: 0.4 * UIView.animationDurationFactor(), - isWraparound: false//previousComponent.level < component.level + duration: duration * UIView.animationDurationFactor(), + wraparound: wraparound ) self.updateAnimations() } @@ -248,20 +470,19 @@ final class ProfileLevelRatingBarComponent: Component { self.barForeground.image = self.barBackground.image } - self.barBackground.tintColor = component.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5) - self.barForeground.tintColor = component.theme.list.itemCheckColors.fillColor - if self.barBackground.superview == nil { self.addSubview(self.barBackground) self.addSubview(self.backgroundClippingContainer) + self.addSubview(self.foregroundBarClippingContainer) + self.foregroundBarClippingContainer.addSubview(self.barForeground) + self.addSubview(self.foregroundClippingContainer) - self.foregroundClippingContainer.addSubview(self.barForeground) } let progressValue: CGFloat - if let animationState = self.animationState { - progressValue = animationState.value(at: CACurrentMediaTime()) + if let testFraction = self.testFraction { + progressValue = testFraction } else { progressValue = component.value } @@ -269,18 +490,84 @@ final class ProfileLevelRatingBarComponent: Component { let barBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - barHeight), size: CGSize(width: availableSize.width, height: barHeight)) transition.setFrame(view: self.barBackground, frame: barBackgroundFrame) - let barForegroundFrame = CGRect(origin: barBackgroundFrame.origin, size: CGSize(width: floorToScreenPixels(progressValue * barBackgroundFrame.width), height: barBackgroundFrame.height)) + var barForegroundFrame = CGRect(origin: barBackgroundFrame.origin, size: CGSize(width: floorToScreenPixels(progressValue * barBackgroundFrame.width), height: barBackgroundFrame.height)) - var barApparentForegroundFrame = barForegroundFrame - if let animationState = self.animationState, animationState.isWraparound { - let progressValue = animationState.wrapAroundValue(at: CACurrentMediaTime(), topValue: 1.0) - barApparentForegroundFrame = CGRect(origin: barBackgroundFrame.origin, size: CGSize(width: floorToScreenPixels(progressValue * barBackgroundFrame.width), height: barBackgroundFrame.height)) + var foregroundAlpha: CGFloat = 1.0 + var foregroundContentsAlpha: CGFloat = 1.0 + var badgeScale: CGFloat = 1.0 + var currentIsNegativeRating: Bool = component.level < 0 + var leftLabelText = component.leftLabel + var rightLabelText = component.rightLabel + + if let animationState = self.animationState { + if let wraparound = animationState.wraparound { + let (step, progress) = animationState.stepFraction(at: CACurrentMediaTime()) + if step == 0 { + currentIsNegativeRating = animationState.fromLevel < 0 + leftLabelText = animationState.fromLeftLabelText + rightLabelText = animationState.fromRightLabelText + } else { + currentIsNegativeRating = animationState.toLevel < 0 + } + let wraparoundEnd: CGFloat + switch wraparound { + case .left: + wraparoundEnd = 0.0 + if step == 0 { + foregroundContentsAlpha = 1.0 * (1.0 - progress) + badgeScale = 1.0 * (1.0 - progress) + 0.3 * progress + } else { + foregroundAlpha = 1.0 * progress + foregroundContentsAlpha = foregroundAlpha + badgeScale = 1.0 * progress + 0.3 * (1.0 - progress) + } + case .right: + wraparoundEnd = 1.0 + if step == 0 { + foregroundAlpha = 1.0 * (1.0 - progress) + foregroundContentsAlpha = foregroundAlpha + badgeScale = 1.0 * (1.0 - progress) + 0.3 * progress + } else { + foregroundContentsAlpha = 1.0 * progress + badgeScale = 1.0 * progress + 0.3 * (1.0 - progress) + } + } + + let progressValue = animationState.wrapAroundValue(at: CACurrentMediaTime(), bottomValue: 1.0 - wraparoundEnd, topValue: wraparoundEnd) + barForegroundFrame = CGRect(origin: barBackgroundFrame.origin, size: CGSize(width: floorToScreenPixels(progressValue * barBackgroundFrame.width), height: barBackgroundFrame.height)) + } else { + let progressValue = animationState.value(at: CACurrentMediaTime()) + barForegroundFrame = CGRect(origin: barBackgroundFrame.origin, size: CGSize(width: floorToScreenPixels(progressValue * barBackgroundFrame.width), height: barBackgroundFrame.height)) + } } - transition.setFrame(view: self.foregroundClippingContainer, frame: barApparentForegroundFrame) - let backgroundClippingFrame = CGRect(origin: CGPoint(x: barBackgroundFrame.minX + barApparentForegroundFrame.width, y: barBackgroundFrame.minY), size: CGSize(width: barBackgroundFrame.width - barApparentForegroundFrame.width, height: barBackgroundFrame.height)) + let badgeColor: UIColor + if currentIsNegativeRating { + badgeColor = UIColor(rgb: 0xFF3B30) + } else { + badgeColor = component.theme.list.itemCheckColors.fillColor + } + + self.barBackground.tintColor = component.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5) + self.barForeground.tintColor = badgeColor + + var effectiveBarForegroundFrame = barForegroundFrame + if currentIsNegativeRating { + effectiveBarForegroundFrame.size.width = barBackgroundFrame.maxX - barForegroundFrame.maxX + effectiveBarForegroundFrame.origin.x = barBackgroundFrame.maxX - effectiveBarForegroundFrame.width + } + transition.setPosition(view: self.foregroundBarClippingContainer, position: effectiveBarForegroundFrame.center) + transition.setBounds(view: self.foregroundBarClippingContainer, bounds: CGRect(origin: CGPoint(x: effectiveBarForegroundFrame.minX - barForegroundFrame.minX, y: 0.0), size: effectiveBarForegroundFrame.size)) + transition.setPosition(view: self.foregroundClippingContainer, position: effectiveBarForegroundFrame.center) + transition.setBounds(view: self.foregroundClippingContainer, bounds: CGRect(origin: CGPoint(x: effectiveBarForegroundFrame.minX - barForegroundFrame.minX, y: 0.0), size: effectiveBarForegroundFrame.size)) + + transition.setAlpha(view: self.foregroundBarClippingContainer, alpha: foregroundAlpha) + transition.setAlpha(view: self.foregroundClippingContainer, alpha: foregroundContentsAlpha) + + let backgroundClippingFrame = CGRect(origin: CGPoint(x: barBackgroundFrame.minX + barForegroundFrame.width, y: barBackgroundFrame.minY), size: CGSize(width: barBackgroundFrame.width - barForegroundFrame.width, height: barBackgroundFrame.height)) transition.setPosition(view: self.backgroundClippingContainer, position: backgroundClippingFrame.center) transition.setBounds(view: self.backgroundClippingContainer, bounds: CGRect(origin: CGPoint(x: backgroundClippingFrame.minX - barBackgroundFrame.minX, y: 0.0), size: backgroundClippingFrame.size)) + transition.setAlpha(view: self.backgroundClippingContainer, alpha: foregroundContentsAlpha) transition.setFrame(view: self.barForeground, frame: CGRect(origin: CGPoint(), size: barBackgroundFrame.size)) @@ -288,52 +575,32 @@ final class ProfileLevelRatingBarComponent: Component { let leftLabelSize = self.backgroundLeftLabel.update( transition: labelsTransition, - component: AnyComponent(AnimatedTextComponent( - font: labelFont, - color: component.theme.list.itemPrimaryTextColor, - items: [AnimatedTextComponent.Item( - id: AnyHashable(0), - content: .text(component.leftLabel) - )] + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: leftLabelText, font: labelFont, textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0) ) let _ = self.foregroundLeftLabel.update( transition: labelsTransition, - component: AnyComponent(AnimatedTextComponent( - font: labelFont, - color: component.theme.list.itemCheckColors.foregroundColor, - items: [AnimatedTextComponent.Item( - id: AnyHashable(0), - content: .text(component.leftLabel) - )] + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: leftLabelText, font: labelFont, textColor: component.theme.list.itemCheckColors.foregroundColor)) )), environment: {}, containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0) ) let rightLabelSize = self.backgroundRightLabel.update( transition: labelsTransition, - component: AnyComponent(AnimatedTextComponent( - font: labelFont, - color: component.theme.list.itemPrimaryTextColor, - items: [AnimatedTextComponent.Item( - id: AnyHashable(0), - content: .text(component.rightLabel) - )] + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: rightLabelText, font: labelFont, textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0) ) let _ = self.foregroundRightLabel.update( transition: labelsTransition, - component: AnyComponent(AnimatedTextComponent( - font: labelFont, - color: component.theme.list.itemCheckColors.foregroundColor, - items: [AnimatedTextComponent.Item( - id: AnyHashable(0), - content: .text(component.rightLabel) - )] + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: rightLabelText, font: labelFont, textColor: component.theme.list.itemCheckColors.foregroundColor)) )), environment: {}, containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0) @@ -379,8 +646,8 @@ final class ProfileLevelRatingBarComponent: Component { transition: transition.withUserData(ProfileLevelRatingBarBadge.TransitionHint(animateText: !labelsTransition.animation.isImmediate)), component: AnyComponent(ProfileLevelRatingBarBadge( theme: component.theme, - title: "\(component.badgeValue)", - suffix: component.badgeTotal + title: component.level < 0 ? "" : "\(component.badgeValue)", + suffix: component.level < 0 ? nil : component.badgeTotal )), environment: {}, containerSize: CGSize(width: 200.0, height: 200.0) @@ -412,9 +679,24 @@ final class ProfileLevelRatingBarComponent: Component { } badgeFrame.origin.x += badgeOverflowWidth - badgeView.frame = badgeFrame + let badgeTailOffset = (barBackgroundFrame.minX + barForegroundFrame.width) - badgeFrame.minX + let badgePosition = CGPoint(x: badgeFrame.minX + badgeTailOffset, y: badgeFrame.maxY) - badgeView.adjustTail(size: apparentBadgeSize, overflowWidth: -badgeOverflowWidth, transition: transition) + if let animationDeltaTime = self.animationDeltaTime, self.animationState != nil, !self.animationIsMovingOverStep { + let previousX = badgeView.center.x + self.addBadgeDeltaX(value: badgePosition.x - previousX, deltaTime: animationDeltaTime) + } + + badgeView.center = badgePosition + badgeView.bounds = CGRect(origin: CGPoint(), size: badgeFrame.size) + transition.setAnchorPoint(layer: badgeView.layer, anchorPoint: CGPoint(x: max(0.0, min(1.0, badgeTailOffset / badgeFrame.width)), y: 1.0)) + + badgeView.updateColors(background: badgeColor) + + badgeView.adjustTail(size: apparentBadgeSize, tailOffset: badgeTailOffset, transition: transition) + transition.setAlpha(view: badgeView, alpha: foregroundContentsAlpha) + self.badgeScale = badgeScale + self.updateBadgeTransform() } return availableSize diff --git a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift index 3db8d0b7bc..04691de605 100644 --- a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift +++ b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift @@ -1028,7 +1028,18 @@ private final class SendInviteLinkScreenComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } controller.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: false, text: text), elevatedLayout: false, action: { _ in return false }), in: .window(.root)) - controller.dismiss() + let navigationController = controller.navigationController as? NavigationController + + let context = component.context + controller.dismiss(completion: { [weak navigationController] in + if let navigationController, let peer = selectedPeers.first?.peer { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: context, + chatLocation: .peer(peer) + )) + } + }) } ) } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift index 049f96e0a4..1bc64a2a86 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift @@ -449,6 +449,7 @@ final class UserAppearanceScreenComponent: Component { if let file = self.cachedIconFiles[fileId], let patternFile = self.cachedIconFiles[patternFileId], let numberString = slugComponents.last, let number = Int32(numberString) { let gift = StarGift.UniqueGift( id: id, + giftId: 0, title: title, number: number, slug: slug, @@ -462,7 +463,11 @@ final class UserAppearanceScreenComponent: Component { giftAddress: nil, resellAmounts: nil, resellForTonOnly: false, - releasedBy: nil + releasedBy: nil, + valueAmount: nil, + valueCurrency: nil, + flags: [], + themePeerId: nil ) signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate) } else { diff --git a/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift b/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift index 3e9a18b768..6af509eee1 100644 --- a/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift +++ b/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift @@ -273,9 +273,9 @@ public final class SettingsThemeWallpaperNode: ASDisplayNode { self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white) } imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true) - |> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in - if let value = value { - return .single(value) + |> mapToSignal { generatorAndRects -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in + if let (generator, _) = generatorAndRects { + return .single(generator) } else { return .complete() } diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index d1b3fb04d6..76d7710925 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -963,7 +963,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, 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, subscriberCount: nil, verificationIconFileId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, 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, subscriberCount: nil, verificationIconFileId: nil)) diff --git a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift index 97e399a610..6a5acba10c 100644 --- a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift @@ -676,7 +676,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { case let .gift(peerId): purpose = .starsGift(peerId: peerId, count: product.count, currency: currency, amount: amount) default: - purpose = .stars(count: product.count, currency: currency, amount: amount) + purpose = .stars(count: product.count, currency: currency, amount: amount, peerId: self.purpose.commercialPeerId) } let _ = (self.context.engine.payments.canPurchasePremium(purpose: purpose) @@ -1264,6 +1264,21 @@ private extension StarsPurchasePurpose { } } + var commercialPeerId: EnginePeer.Id? { + switch self { + case let .transfer(peerId, _): + return peerId + case let .reactions(peerId, _): + return peerId + case let .subscription(peerId, _, _): + return peerId + case let .sendMessage(peerId, _): + return peerId + default: + return nil + } + } + var requiredStars: Int64? { switch self { case let .topUp(requiredStars, _): diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index 846115bada..9d36fd41a0 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -390,7 +390,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { giftAnimationSubject = .generic(gift.file) giftAvailability = gift.availability case let .unique(gift): - giftAnimationSubject = .unique(gift) + giftAnimationSubject = .unique(nil, gift) } isGiftUpgrade = transaction.flags.contains(.isStarGiftUpgrade) } else if let giveawayMessageIdValue = transaction.giveawayMessageId { diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 0c5af23a24..52d60129f8 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -673,14 +673,13 @@ final class StarsTransactionsScreenComponent: Component { let withdrawAvailable = (self.revenueState?.balances.overallRevenue.amount.value ?? 0) > 0 if component.starsContext.ton { - //TODO:localize let proceedsSize = self.proceedsView.update( transition: .immediate, component: AnyComponent(ListSectionComponent( theme: environment.theme, header: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( - string: "Proceeds Overview".uppercased(), + string: environment.strings.Ton_ProceedsOverview.uppercased(), font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor )), @@ -691,14 +690,14 @@ final class StarsTransactionsScreenComponent: Component { AnyComponentWithIdentity(id: 0, component: AnyComponent(StarsOverviewItemComponent( theme: environment.theme, dateTimeFormat: environment.dateTimeFormat, - title: "Balance Available to Withdraw", + title: environment.strings.Ton_AvailableBalance, value: self.revenueState?.balances.availableBalance ?? CurrencyAmount(amount: .zero, currency: .ton), rate: self.revenueState?.usdRate ?? 0.0 ))), AnyComponentWithIdentity(id: 1, component: AnyComponent(StarsOverviewItemComponent( theme: environment.theme, dateTimeFormat: environment.dateTimeFormat, - title: "Total Lifetime Proceeds", + title: environment.strings.Ton_LifetimeProceeds, value: self.revenueState?.balances.overallRevenue ?? CurrencyAmount(amount: .zero, currency: .ton), rate: self.revenueState?.usdRate ?? 0.0 ))) @@ -725,7 +724,7 @@ final class StarsTransactionsScreenComponent: Component { return (TelegramTextAttributes.URL, contents) }) - let balanceInfoRawString = "Collect your TON using Fragment. [Learn More >]()" + let balanceInfoRawString = environment.strings.Ton_WithdrawViaFragment_Info let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(balanceInfoRawString, attributes: termsMarkdownAttributes, textAlignment: .natural)) if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme { self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) @@ -754,7 +753,7 @@ final class StarsTransactionsScreenComponent: Component { }, tapAction: { [weak self] attributes, _ in if let controller = self?.controller?() as? StarsTransactionsScreen, let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: environment.strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: component.starsContext.ton ? environment.strings.Ton_WithdrawViaFragment_Info_URL : environment.strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) } } )) : nil, @@ -766,7 +765,7 @@ final class StarsTransactionsScreenComponent: Component { count: self.starsState?.balance ?? StarsAmount.zero, currency: component.starsContext.ton ? .ton : .stars, rate: nil, - actionTitle: component.starsContext.ton ? "Withdraw via Fragment" : (withdrawAvailable ? environment.strings.Stars_Intro_BuyShort : environment.strings.Stars_Intro_Buy), + actionTitle: component.starsContext.ton ? environment.strings.Ton_WithdrawViaFragment : (withdrawAvailable ? environment.strings.Stars_Intro_BuyShort : environment.strings.Stars_Intro_Buy), actionAvailable: (!premiumConfiguration.areStarsDisabled && !premiumConfiguration.isPremiumDisabled), actionIsEnabled: true, actionIcon: component.starsContext.ton ? nil : PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), @@ -1286,11 +1285,24 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { } if subscription.untilDate > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) { var updated = false - if let channel = subscription.peer._asPeer() as? TelegramChannel, channel.participationStatus == .left && !subscription.flags.contains(.isCancelled) { + + var hasLeft = false + var isKicked = false + if let channel = subscription.peer._asPeer() as? TelegramChannel { + switch channel.participationStatus { + case .left: + hasLeft = true + case .kicked: + isKicked = true + default: + break + } + } + + if hasLeft && !subscription.flags.contains(.isCancelled) { let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone() updated = true - } - if let _ = subscription.inviteHash, !subscription.flags.contains(.isCancelledByBot) { + } else if let _ = subscription.inviteHash, hasLeft && !isKicked && !subscription.flags.contains(.isCancelledByBot) { let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone() updated = true } diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index 2a94368553..357c09f126 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -854,7 +854,7 @@ public final class PeerListItemComponent: Component { } else if peer.isFake { statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus { - statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) + statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(0)) if let color = emojiStatus.color { particleColor = UIColor(rgb: UInt32(bitPattern: color)) } diff --git a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift index 81ede5b80c..38d0fac01e 100644 --- a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift +++ b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift @@ -53,14 +53,16 @@ public final class TabSelectorComponent: Component { public var font: UIFont public var spacing: CGFloat public var innerSpacing: CGFloat? + public var fillWidth: Bool public var lineSelection: Bool public var verticalInset: CGFloat public var allowScroll: Bool - public init(font: UIFont, spacing: CGFloat, innerSpacing: CGFloat? = nil, lineSelection: Bool = false, verticalInset: CGFloat = 0.0, allowScroll: Bool = true) { + public init(font: UIFont, spacing: CGFloat, innerSpacing: CGFloat? = nil, fillWidth: Bool = false, lineSelection: Bool = false, verticalInset: CGFloat = 0.0, allowScroll: Bool = true) { self.font = font self.spacing = spacing self.innerSpacing = innerSpacing + self.fillWidth = fillWidth self.lineSelection = lineSelection self.verticalInset = verticalInset self.allowScroll = allowScroll @@ -506,6 +508,13 @@ public final class TabSelectorComponent: Component { return nil } + public func frameForItem(_ id: AnyHashable) -> CGRect? { + if let item = self.visibleItems[id] { + return item.convert(item.bounds, to: self) + } + return nil + } + func update(component: TabSelectorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let selectionColorUpdated = component.colors.selection != self.component?.colors.selection @@ -611,7 +620,7 @@ public final class TabSelectorComponent: Component { } let itemSize = itemView.title.update( - transition: .immediate, + transition: itemTransition, component: AnyComponent(ItemComponent( context: component.context, content: item.content, @@ -630,7 +639,9 @@ public final class TabSelectorComponent: Component { } let estimatedContentWidth = 2.0 * spacing + innerContentWidth + (CGFloat(component.items.count - 1) * (spacing + innerInset)) - if estimatedContentWidth > availableSize.width && !allowScroll { + if component.customLayout?.fillWidth == true && estimatedContentWidth < availableSize.width && component.items.count > 1 { + spacing = (availableSize.width - innerContentWidth) / CGFloat(component.items.count + 1) - innerInset * 2.0 + } else if estimatedContentWidth > availableSize.width && !allowScroll { spacing = (availableSize.width - innerContentWidth) / CGFloat(component.items.count + 1) innerInset = 0.0 } @@ -736,7 +747,7 @@ public final class TabSelectorComponent: Component { } var mappedSelectionFrame = effectiveBackgroundRect.insetBy(dx: innerInset, dy: 0.0) - mappedSelectionFrame.origin.y = mappedSelectionFrame.maxY + 6.0 + mappedSelectionFrame.origin.y = mappedSelectionFrame.maxY + 7.0 mappedSelectionFrame.size.height = 3.0 transition.setPosition(view: self.selectionView, position: mappedSelectionFrame.center) transition.setBounds(view: self.selectionView, bounds: CGRect(origin: CGPoint(), size: mappedSelectionFrame.size)) diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/Contents.json new file mode 100644 index 0000000000..562f9b14b9 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TimeLocked.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/TimeLocked.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/TimeLocked.pdf new file mode 100644 index 0000000000..5a20922cb0 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/DateLockedIcon.imageset/TimeLocked.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/Contents.json new file mode 100644 index 0000000000..2bbef9aa48 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "badratingprofile.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/badratingprofile.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/badratingprofile.pdf new file mode 100644 index 0000000000..f52ed6fbbe Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/InlineRatingWarning.imageset/badratingprofile.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/Contents.json new file mode 100644 index 0000000000..93248aa458 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "badratingslider.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/badratingslider.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/badratingslider.pdf new file mode 100644 index 0000000000..dbd046137a Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/ProfileLevelWarningIcon.imageset/badratingslider.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/Contents.json new file mode 100644 index 0000000000..8b91afdd9f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "profilemusic_20.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/profilemusic_20.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/profilemusic_20.pdf new file mode 100644 index 0000000000..35a49f76c7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/SaveMusic.imageset/profilemusic_20.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/Contents.json new file mode 100644 index 0000000000..8c3342fe88 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "profilemusic_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/profilemusic_30.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/profilemusic_30.pdf new file mode 100644 index 0000000000..286ad31c81 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/SavedMusic.imageset/profilemusic_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/Contents.json new file mode 100644 index 0000000000..3bd65d39c2 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "rotate_18.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/rotate_18.pdf b/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/rotate_18.pdf new file mode 100644 index 0000000000..34df5f002f Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/rotate_18.pdf differ diff --git a/submodules/TelegramUI/Resources/Animations/anim_gift.tgs b/submodules/TelegramUI/Resources/Animations/anim_gift.tgs new file mode 100644 index 0000000000..1a0c0d6cf8 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/anim_gift.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/badge_with_tail.json b/submodules/TelegramUI/Resources/Animations/badge_with_tail.json index 56717e56b5..7129fda312 100644 --- a/submodules/TelegramUI/Resources/Animations/badge_with_tail.json +++ b/submodules/TelegramUI/Resources/Animations/badge_with_tail.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":360,"w":780,"h":600,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"1 Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[390,300,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":181,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[22.824,0]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.265,-11.424],[-132.5,0]],"v":[[-390,-60],[390,-60],[150,180],[102.53,197.841],[28.313,273.107],[-28.313,273.107],[-102.53,197.841],[-150,180]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":270,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[29.086,29.579]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.264,-11.424],[-29.086,-29.579]],"v":[[-390.002,-60],[390,-60],[-40,180],[-87.47,197.841],[-161.687,273.107],[-218.313,273.107],[-292.53,197.841],[-350,140]],"c":true}]},{"t":359,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[0,20.894],[0,0],[0,32.083]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[0,-17.841],[0,-32.083]],"v":[[-390,-60],[390,-60],[-240,180],[-287.47,197.841],[-361.687,273.107],[-390,263.672],[-390,197.841],[-390,140]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":360,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Blob 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[390,300,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[132.548,0]],"v":[[390,-60],[0,-60],[0,-300],[150,-300]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RT","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[132.493,0],[0,0]],"v":[[390,-60],[0,-60],[0,180],[150.1,180],[390,-59.9]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RB","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[-132.548,0]],"v":[[-390,-60],[0,-60],[0,-300],[-150,-300]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LT","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":181,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[-132.493,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-150.1,180],[-390,-59.9]],"c":true}]},{"t":359,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,66.274]],"o":[[0,0],[0,0],[0,0],[-66.274,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-270,180],[-390,60]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LB","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":360,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[390,300,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[0,20.894],[0,0],[0,32.083]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[0,-17.841],[0,-32.083]],"v":[[-390,-60],[390,-60],[-240,180],[-287.47,197.841],[-361.687,273.107],[-390,263.672],[-390,197.841],[-390,140]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[29.086,29.579]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.264,-11.424],[-29.086,-29.579]],"v":[[-390.002,-60],[390,-60],[-40,180],[-87.47,197.841],[-161.687,273.107],[-218.313,273.107],[-292.53,197.841],[-350,140]],"c":true}]},{"t":179,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[22.824,0]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.265,-11.424],[-132.5,0]],"v":[[-390,-60],[390,-60],[150,180],[102.53,197.841],[28.313,273.107],[-28.313,273.107],[-102.53,197.841],[-150,180]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Blob","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[390,300,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[132.548,0]],"v":[[390,-60],[0,-60],[0,-300],[150,-300]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RT","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[132.493,0],[0,0]],"v":[[390,-60],[0,-60],[0,180],[150.1,180],[390,-59.9]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RB","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[-132.548,0]],"v":[[-390,-60],[0,-60],[0,-300],[-150,-300]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LT","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,66.274]],"o":[[0,0],[0,0],[0,0],[-66.274,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-270,180],[-390,60]],"c":true}]},{"t":179,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[-132.493,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-150.1,180],[-390,-59.9]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LB","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":360,"w":78,"h":60,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"1 Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39,30,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[-10,10,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[22.824,0]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.265,-11.424],[-132.5,0]],"v":[[-390,-60],[390,-60],[150,180],[102.53,197.841],[28.313,273.107],[-28.313,273.107],[-102.53,197.841],[-150,180]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":270,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[29.086,29.579]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.264,-11.424],[-29.086,-29.579]],"v":[[-390.002,-60],[390,-60],[-40,180],[-87.47,197.841],[-161.687,273.107],[-218.313,273.107],[-292.53,197.841],[-350,140]],"c":true}]},{"t":359,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[0,20.894],[0,0],[0,32.083]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[0,-17.841],[0,-32.083]],"v":[[-390,-60],[390,-60],[-240,180],[-287.47,197.841],[-361.687,273.107],[-390,263.672],[-390,197.841],[-390,140]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":360,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Blob 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39,30,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[-10,10,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[132.548,0]],"v":[[390,-60],[0,-60],[0,-300],[150,-300]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RT","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[132.493,0],[0,0]],"v":[[390,-60],[0,-60],[0,180],[150.1,180],[390,-59.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RB","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[-132.548,0]],"v":[[-390,-60],[0,-60],[0,-300],[-150,-300]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LT","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[-132.493,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-150.1,180],[-390,-59.9]],"c":true}]},{"t":359,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,66.274]],"o":[[0,0],[0,0],[0,0],[-66.274,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-270,180],[-390,60]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LB","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":360,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39,30,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[10,10,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":1,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[0,20.894],[0,0],[0,32.083]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[0,-17.841],[0,-32.083]],"v":[[-390,-60],[390,-60],[-240,180],[-287.47,197.841],[-361.687,273.107],[-390,263.672],[-390,197.841],[-390,140]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":90,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[29.086,29.579]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.264,-11.424],[-29.086,-29.579]],"v":[[-390.002,-60],[390,-60],[-40,180],[-87.47,197.841],[-161.687,273.107],[-218.313,273.107],[-292.53,197.841],[-350,140]],"c":true}]},{"t":179,"s":[{"i":[[0,132.5],[0,0],[132.5,0],[11.264,-11.424],[0,0],[15.637,15.858],[0,0],[22.824,0]],"o":[[0,0],[0,132.548],[-22.081,0],[0,0],[-15.637,15.858],[0,0],[-11.265,-11.424],[-132.5,0]],"v":[[-390,-60],[390,-60],[150,180],[102.53,197.841],[28.313,273.107],[-28.313,273.107],[-102.53,197.841],[-150,180]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Blob","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39,30,0],"ix":2,"l":2},"a":{"a":0,"k":[390,300,0],"ix":1,"l":2},"s":{"a":0,"k":[10,10,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[132.548,0]],"v":[[390,-60],[0,-60],[0,-300],[150,-300]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RT","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[132.493,0],[0,0]],"v":[[390,-60],[0,-60],[0,180],[150.1,180],[390,-59.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RB","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-132.548],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[-132.548,0]],"v":[[-390,-60],[0,-60],[0,-300],[-150,-300]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LT","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":1,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,66.274]],"o":[[0,0],[0,0],[0,0],[-66.274,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-270,180],[-390,60]],"c":true}]},{"t":179,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,132.493]],"o":[[0,0],[0,0],[0,0],[-132.493,0],[0,0]],"v":[[-390,-60],[0,-60],[0,180],[-150.1,180],[-390,-59.9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[390,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"LB","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 602daed2a3..a0ea346e17 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -125,11 +125,14 @@ import PostSuggestionsSettingsScreen import ChatSendStarsScreen extension ChatControllerImpl { - func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((Bool) -> Void) -> Void) { + func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((ContainedViewLayoutTransition?) -> Void) -> Void) { self.contentDataReady.set(false) self.contentDataDisposable?.dispose() + self.newTopicEventsDisposable?.dispose() + self.newTopicEventsDisposable = nil + let configuration: Signal = self.presentationInterfaceStatePromise.get() |> map { presentationInterfaceState -> ChatControllerImpl.ContentData.Configuration in return ChatControllerImpl.ContentData.Configuration( @@ -163,16 +166,27 @@ extension ChatControllerImpl { return } - apply({ [weak self, weak contentData] forceAnimation in + if let historyNodeData = contentData.state.historyNodeData { + self.subject = nil + self.updateChatLocationToOther(chatLocation: historyNodeData.chatLocation) + return + } else if case let .botForumThread(linkedForumId, threadId) = self.subject { + self.subject = nil + self.updateInitialChatBotForumLocationThread(linkedForumId: linkedForumId, threadId: threadId) + return + } + + apply({ [weak self, weak contentData] forceAnimationTransition in guard let self, let contentData, self.pendingContentData?.contentData === contentData else { return } self.contentData = contentData self.pendingContentData = nil - self.contentDataUpdated(synchronous: true, forceAnimation: forceAnimation, previousState: contentData.state) - self.chatThemeEmoticonPromise.set(contentData.chatThemeEmoticonPromise.get()) + self.contentDataUpdated(synchronous: true, forceAnimationTransition: forceAnimationTransition, previousState: contentData.state) + + self.chatThemePromise.set(contentData.chatThemePromise.get()) self.chatWallpaperPromise.set(contentData.chatWallpaperPromise.get()) if let historyNode { @@ -187,13 +201,42 @@ extension ChatControllerImpl { guard let self, let contentData, self.contentData === contentData else { return } - self.contentDataUpdated(synchronous: false, forceAnimation: false, previousState: previousState) + + if let historyNodeData = contentData.state.historyNodeData { + self.updateChatLocationToOther(chatLocation: historyNodeData.chatLocation) + return + } + + self.contentDataUpdated(synchronous: false, forceAnimationTransition: nil, previousState: previousState) } }) + + if self.newTopicEventsDisposable == nil, let peerId = chatLocation.peerId, chatLocation.threadId == EngineMessage.newTopicThreadId { + self.newTopicEventsDisposable = (self.context.account.pendingMessageManager.newTopicEvents(peerId: peerId) + |> mapToSignal { event -> Signal in + if case let .didMove(fromThreadId, toThreadId) = event { + if fromThreadId == EngineMessage.newTopicThreadId { + return .single(toThreadId) + } + } + return .never() + } + |> take(1) + |> deliverOnMainQueue).startStrict(next: { [weak self] threadId in + guard let self else { + return + } + if chatLocation.peerId != peerId { + self.updateInitialChatBotForumLocationThread(linkedForumId: peerId, threadId: threadId) + } else { + self.updateChatLocationThread(threadId: threadId, animationDirection: nil, replaceInline: true, transferInputState: true) + } + }) + } }) } - func contentDataUpdated(synchronous: Bool, forceAnimation: Bool, previousState: ContentData.State) { + func contentDataUpdated(synchronous: Bool, forceAnimationTransition: ContainedViewLayoutTransition?, previousState: ContentData.State) { guard let contentData = self.contentData else { return } @@ -351,11 +394,15 @@ extension ChatControllerImpl { if previousState.pinnedMessage != contentData.state.pinnedMessage { animated = true } - if forceAnimation { - animated = true + var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate + if let forceAnimationTransition { + transition = forceAnimationTransition + } + if !self.willAppear { + transition = .immediate } - self.updateChatPresentationInterfaceState(animated: animated && self.willAppear, interactive: false, { presentationInterfaceState in + self.updateChatPresentationInterfaceState(transition: transition, interactive: false, { presentationInterfaceState in var presentationInterfaceState = presentationInterfaceState presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in return contentData.state.renderedPeer @@ -626,7 +673,11 @@ extension ChatControllerImpl { if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, self.presentationInterfaceState.persistentData.topicListPanelLocation == true, self.presentationInterfaceState.chatLocation.threadId != nil { self.updateChatLocationThread(threadId: nil, animationDirection: .left) } else { - self.dismiss() + if self.attemptNavigation({ [weak self] in + self?.dismiss() + }) { + self.dismiss() + } } } @@ -797,32 +848,17 @@ extension ChatControllerImpl { } if let peerId = self.chatLocation.peerId { - self.chatThemeEmoticonPromise.set(self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThemeEmoticon(id: peerId))) + self.chatThemePromise.set(self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ChatTheme(id: peerId))) let chatWallpaper = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Wallpaper(id: peerId)) |> take(1) self.chatWallpaperPromise.set(chatWallpaper) } else { - self.chatThemeEmoticonPromise.set(.single(nil)) + self.chatThemePromise.set(.single(nil)) self.chatWallpaperPromise.set(.single(nil)) } self.reloadCachedData() - self.historyStateDisposable = self.chatDisplayNode.historyNode.historyState.get().startStrict(next: { [weak self] state in - if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: strongSelf.isViewLoaded && strongSelf.view.window != nil, { - $0.updatedChatHistoryState(state) - }) - - if let botStart = strongSelf.botStart, case let .loaded(isEmpty, _) = state { - strongSelf.botStart = nil - if !isEmpty { - strongSelf.startBot(botStart.payload) - } - } - } - }) - if self.context.sharedContext.immediateExperimentalUISettings.crashOnLongQueries { let _ = (self.ready.get() |> filter({ $0 }) @@ -4532,6 +4568,22 @@ extension ChatControllerImpl { } func setupChatHistoryNode(historyNode: ChatHistoryListNodeImpl) { + self.historyStateDisposable?.dispose() + self.historyStateDisposable = historyNode.historyState.get().startStrict(next: { [weak self] state in + if let strongSelf = self { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: strongSelf.isViewLoaded && strongSelf.view.window != nil, { + $0.updatedChatHistoryState(state) + }) + + if let botStart = strongSelf.botStart, case let .loaded(isEmpty, _) = state { + strongSelf.botStart = nil + if !isEmpty { + strongSelf.startBot(botStart.payload) + } + } + } + }) + do { let peerId = self.chatLocation.peerId if let subject = self.subject, case .scheduledMessages = subject { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift index 951d94b1f2..93263e13fa 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift @@ -123,6 +123,7 @@ import ChatEmptyNode import ChatMediaInputStickerGridItem import AdsInfoScreen import FaceScanScreen +import ForumCreateTopicScreen extension ChatControllerImpl { func openPeer(peer: EnginePeer?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, fromReactionMessageId: MessageId? = nil, expandAvatar: Bool = false, peerTypes: ReplyMarkupButtonAction.PeerTypes? = nil, skipAgeVerification: Bool = false) { @@ -328,4 +329,106 @@ extension ChatControllerImpl { } }) } + + func openBotForumMoreMenu(sourceView: UIView, gesture: ContextGesture?) { + Task { @MainActor [weak self] in + guard let self, let peerId = self.chatLocation.peerId else { + return + } + guard let forumPeerId = await (self.context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: peerId) + ) + |> map { value -> EnginePeer.Id? in + if case let .known(value) = value { + return value + } else { + return nil + } + }).get() else { + return + } + + let strings = self.presentationData.strings + + var items: [ContextMenuItem] = [] + + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "Open Profile", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let self, let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else { + return + } + + guard let controller = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else { + return + } + (self.navigationController as? NavigationController)?.pushViewController(controller) + }))) + + items.append(.separator) + items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] action in + action.dismissWithResult(.default) + + self?.beginMessageSearch("") + }))) + + items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] action in + guard let self else { + return + } + + action.dismissWithResult(.default) + + let controller = ForumCreateTopicScreen(context: self.context, peerId: forumPeerId, mode: .create) + controller.navigationPresentation = .modal + + controller.completion = { [weak self, weak controller] title, fileId, iconColor, _ in + controller?.isInProgress = true + controller?.view.endEditing(true) + + guard let self else { + return + } + + let _ = (self.context.engine.peers.createForumChannelTopic(id: forumPeerId, title: title, iconColor: iconColor, iconFileId: fileId) + |> deliverOnMainQueue).startStandalone(next: { [weak self, weak controller] topicId in + guard let self else { + return + } + self.updateChatLocationThread(threadId: topicId) + controller?.dismiss() + }, error: { _ in + controller?.isInProgress = false + }) + } + self.push(controller) + }))) + + let presentationData = self.presentationData + + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + self.presentInGlobalOverlay(contextController) + } + } +} + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceView: UIView + + init(controller: ViewController, sourceView: UIView) { + self.controller = controller + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) + } } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index 083f47e8d5..adbc9b3266 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -100,10 +100,23 @@ func openWebAppImpl( } } } + + var botPeer = botPeer + if case let .inline(bot) = source { + botPeer = bot + } - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.BotAppSettings(id: botPeer.id)) - |> deliverOnMainQueue).start(next: { appSettings in - let openWebView = { [weak parentController] in + let _ = combineLatest(queue: Queue.mainQueue(), + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.BotAppSettings(id: botPeer.id)), + ApplicationSpecificNotice.getBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id), + context.engine.messages.attachMenuBots(), + context.engine.messages.getAttachMenuBot(botId: botPeer.id, cached: true) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + ).start(next: { appSettings, noticed, attachMenuBots, attachMenuBot in + let openWebView: (Bool) -> Void = { [weak parentController] justInstalled in guard let parentController else { return } @@ -305,6 +318,11 @@ func openWebAppImpl( presentImpl = { [weak controller] c, a in controller?.present(c, in: .window(.root), with: a) } + + if justInstalled { + let content: UndoOverlayContent = .succeed(text: presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil) + controller.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current) + } }, error: { [weak parentController] error in if let parentController { parentController.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { @@ -314,25 +332,37 @@ func openWebAppImpl( } } - if skipTermsOfService { - openWebView() - } else { - var botPeer = botPeer - if case let .inline(bot) = source { - botPeer = bot + var isAttachMenuBotInstalled: Bool? + if let _ = attachMenuBot { + if let _ = attachMenuBots.first(where: { $0.peer.id == botPeer.id && !$0.flags.contains(.notActivated) }) { + isAttachMenuBotInstalled = true + } else { + isAttachMenuBotInstalled = false } - let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id) - |> deliverOnMainQueue).startStandalone(next: { [weak parentController] value in - guard let parentController else { - return + } + + if !noticed || attachMenuBot?.flags.contains(.notActivated) == true || isAttachMenuBotInstalled == false { + if let isAttachMenuBotInstalled, let attachMenuBot { + if !isAttachMenuBotInstalled { + let controller = webAppTermsAlertController(context: context, updatedPresentationData: updatedPresentationData, bot: attachMenuBot, completion: { allowWrite in + let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone() + let _ = (context.engine.messages.addBotToAttachMenu(botId: botPeer.id, allowWrite: allowWrite) + |> deliverOnMainQueue).startStandalone(error: { _ in + }, completed: { + openWebView(true) + }) + }) + parentController.present(controller, in: .window(.root)) + } else { + openWebView(false) } - - if value { - openWebView() + } else { + if skipTermsOfService { + openWebView(false) } else { let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, completion: { _ in let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone() - openWebView() + openWebView(false) }, showMore: nil, openTerms: { if let navigationController = parentController.navigationController as? NavigationController { context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) @@ -340,7 +370,9 @@ func openWebAppImpl( }) parentController.present(controller, in: .window(.root)) } - }) + } + } else { + openWebView(false) } }) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerThemeManagement.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerThemeManagement.swift index dadcc49e02..8006a42067 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerThemeManagement.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerThemeManagement.swift @@ -123,6 +123,7 @@ import ChatEmptyNode import ChatMediaInputStickerGridItem import AdsInfoScreen import Photos +import ChatThemeScreen extension ChatControllerImpl { public func presentThemeSelection() { @@ -161,8 +162,8 @@ extension ChatControllerImpl { return animatedEmojiStickers } - let _ = (combineLatest(queue: Queue.mainQueue(), self.chatThemeEmoticonPromise.get(), animatedEmojiStickers) - |> take(1)).startStandalone(next: { [weak self] themeEmoticon, animatedEmojiStickers in + let _ = (combineLatest(queue: Queue.mainQueue(), self.chatThemePromise.get(), animatedEmojiStickers) + |> take(1)).startStandalone(next: { [weak self] chatTheme, animatedEmojiStickers in guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return } @@ -176,21 +177,21 @@ extension ChatControllerImpl { context: context, updatedPresentationData: strongSelf.updatedPresentationData, animatedEmojiStickers: animatedEmojiStickers, - initiallySelectedEmoticon: themeEmoticon, + initiallySelectedTheme: chatTheme, peerName: strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "", canResetWallpaper: canResetWallpaper, - previewTheme: { [weak self] emoticon, dark in + previewTheme: { [weak self] chatTheme, dark in if let strongSelf = self { strongSelf.presentCrossfadeSnapshot() - strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark))) + strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme, dark))) } }, changeWallpaper: { [weak self] in - guard let strongSelf = self, let peerId else { + guard let self, let peerId else { return } - if let themeController = strongSelf.themeScreen { - strongSelf.themeScreen = nil + if let themeController = self.themeScreen { + self.themeScreen = nil themeController.dimTapped() } let dismissControllers = { [weak self] in @@ -206,69 +207,67 @@ extension ChatControllerImpl { } var openWallpaperPickerImpl: ((Bool) -> Void)? let openWallpaperPicker = { [weak self] animateAppearance in - guard let strongSelf = self else { + guard let self else { return } let controller = wallpaperMediaPickerController( - context: strongSelf.context, - updatedPresentationData: strongSelf.updatedPresentationData, + context: context, + updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), animateAppearance: animateAppearance, completion: { [weak self] _, result in - guard let strongSelf = self, let asset = result as? PHAsset else { + guard let self, let asset = result as? PHAsset else { return } - let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) + let controller = WallpaperGalleryController(context: context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness, forBoth in - if let strongSelf = self { - uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, peerId: peerId, forBoth: forBoth, completion: { - Queue.mainQueue().after(0.3, { - dismissControllers() - }) + controller.apply = { wallpaper, options, editedImage, cropRect, brightness, forBoth in + uploadCustomPeerWallpaper(context: context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, peerId: peerId, forBoth: forBoth, completion: { + Queue.mainQueue().after(0.3, { + dismissControllers() }) - } + }) } - strongSelf.push(controller) + self.push(controller) }, openColors: { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), push: { [weak self] controller in - if let strongSelf = self { - strongSelf.push(controller) + let controller = standaloneColorPickerController(context: context, peer: EnginePeer(peer), push: { [weak self] controller in + if let self { + self.push(controller) } }, openGallery: { openWallpaperPickerImpl?(false) }) controller.navigationPresentation = .flatModal - strongSelf.push(controller) + self.push(controller) } ) controller.navigationPresentation = .flatModal - strongSelf.push(controller) + self.push(controller) } openWallpaperPickerImpl = openWallpaperPicker openWallpaperPicker(true) }, resetWallpaper: { [weak self] in - guard let strongSelf = self, let peerId else { + guard let self, let peerId else { return } - let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone() + let _ = self.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone() }, - completion: { [weak self] emoticon in - guard let strongSelf = self, let peerId else { + completion: { [weak self] chatTheme in + guard let self, let peerId else { return } - if canResetWallpaper && emoticon != nil { - let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone() + if canResetWallpaper && chatTheme != nil { + let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: true).startStandalone() } - strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon ?? "", nil))) - let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).startStandalone(completed: { [weak self] in - if let strongSelf = self { - strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((nil, nil))) + strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme ?? .emoticon(""), nil))) + let _ = context.engine.themes.setChatTheme(peerId: peerId, chatTheme: chatTheme ?? .emoticon("")).startStandalone(completed: { [weak self] in + if let self { + self.chatThemeAndDarkAppearancePreviewPromise.set(.single((nil, nil))) } }) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f16850794c..70d9760246 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -141,6 +141,7 @@ import SuggestedPostApproveAlert import AVFoundation import BalanceNeededScreen import FaceScanScreen +import ChatThemeScreen public final class ChatControllerOverlayPresentationData { public let expandData: (ASDisplayNode?, () -> Void) @@ -247,7 +248,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let context: AccountContext public internal(set) var chatLocation: ChatLocation - public let subject: ChatControllerSubject? + public internal(set) var subject: ChatControllerSubject? var botStart: ChatControllerInitialBotStart? var attachBotStart: ChatControllerInitialAttachBotStart? @@ -258,6 +259,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var contentData: ChatControllerImpl.ContentData? let contentDataReady = ValuePromise(false, ignoreRepeated: true) var contentDataDisposable: Disposable? + var newTopicEventsDisposable: Disposable? var didHandlePerformDismissAction: Bool = false var didInitializePersistentPeerInterfaceData: Bool = false @@ -286,7 +288,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return self.presentationInterfaceState.interfaceState.selectionState?.selectedIds } - let chatThemeEmoticonPromise = Promise() + let chatThemePromise = Promise() let chatWallpaperPromise = Promise() var chatTitleView: ChatTitleView? @@ -411,7 +413,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var canReadHistoryDisposable: Disposable? var computedCanReadHistoryPromise = ValuePromise(false, ignoreRepeated: true) - var themeEmoticonAndDarkAppearancePreviewPromise = Promise<(String?, Bool?)>((nil, nil)) + var chatThemeAndDarkAppearancePreviewPromise = Promise<(ChatTheme?, Bool?)>((nil, nil)) var didSetPresentationData = false var presentationData: PresentationData var presentationDataPromise = Promise() @@ -703,6 +705,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.stickerSettings = ChatInterfaceStickerSettings() + var subject = subject + if case .botForumThread = subject { + subject = nil + } + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, greetingData: context.prefetchManager?.preloadedGreetingSticker, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) if case let .customChatContents(customChatContents) = subject { @@ -1104,8 +1111,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.present(BotReceiptController(context: self.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } return true - case .setChatTheme: - self.presentThemeSelection() + case let .setChatTheme(chatTheme): + switch chatTheme { + case .emoticon: + self.presentThemeSelection() + case let .gift(gift, _): + if case let .unique(uniqueGift) = gift { + let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, gift: uniqueGift, shareStory: { [weak self] uniqueGift in + Queue.mainQueue().after(0.15) { + if let self { + let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self) + self.push(controller) + } + } + }, openChatTheme: { [weak self] in + if let self { + Queue.mainQueue().after(0.15) { + self.presentThemeSelection() + } + } + }, dismissed: nil) + self.push(controller) + } + } return true case let .setChatWallpaper(wallpaper, _): guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { @@ -2510,6 +2538,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } return + } else if !message.attributes.contains(where: { attribute in + if let attribute = attribute as? ReplyMarkupMessageAttribute, !attribute.rows.isEmpty { + return true + } else { + return false + } + }), let data = data?.makeData(), data.count == 1 { + let buttonType = data.withUnsafeBytes { buffer -> UInt8 in + return buffer.baseAddress!.assumingMemoryBound(to: UInt8.self).pointee + } + if buttonType == 3 { + if let threadId = message.threadId { + strongSelf.updateChatLocationThread(threadId: threadId, animationDirection: .right) + return + } + } } let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) @@ -4408,29 +4452,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } var file: TelegramMediaFile? - var title: String? - var performer: String? for media in message.media { if let mediaFile = media as? TelegramMediaFile, mediaFile.isMusic { file = mediaFile - for attribute in mediaFile.attributes { - if case let .Audio(_, _, titleValue, performerValue, _) = attribute { - if let titleValue, !titleValue.isEmpty { - title = titleValue - } - if let performerValue, !performerValue.isEmpty { - performer = performerValue - } - } - } } } guard let file else { return } - var signal = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .message(message: MessageReference(message._asMessage()), media: file)) - let disposable: MetaDisposable if let current = self.saveMediaDisposable { disposable = current @@ -4438,108 +4468,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposable = MetaDisposable() self.saveMediaDisposable = disposable } - - var cancelImpl: (() -> Void)? - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { [weak self] subscriber in - guard let self else { - return EmptyDisposable - } - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() + disposable.set( + saveMediaToFiles( + context: self.context, + fileReference: .message(message: MessageReference(message._asMessage()), media: file), + present: { [weak self] c, a in + guard let self else { + return + } + self.present(c, in: .window(.root), with: a) } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.startStrict() - - signal = signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { [weak disposable] in - disposable?.set(nil) - } - disposable.set((signal - |> deliverOnMainQueue).startStrict(next: { [weak self] state, _ in - guard let self else { - return - } - switch state { - case .progress: - break - case let .data(data): - if data.complete { - var symlinkPath = data.path + ".mp3" - if fileSize(symlinkPath) != nil { - try? FileManager.default.removeItem(atPath: symlinkPath) - } - let _ = try? FileManager.default.linkItem(atPath: data.path, toPath: symlinkPath) - - let audioUrl = URL(fileURLWithPath: symlinkPath) - let audioAsset = AVURLAsset(url: audioUrl) - - var fileExtension = "mp3" - if let filename = file.fileName { - if let dotIndex = filename.lastIndex(of: ".") { - fileExtension = String(filename[filename.index(after: dotIndex)...]) - } - } - - var nameComponents: [String] = [] - if let title { - if let performer { - nameComponents.append(performer) - } - nameComponents.append(title) - } else { - var artist: String? - var title: String? - for data in audioAsset.commonMetadata { - if data.commonKey == .commonKeyArtist { - artist = data.stringValue - } - if data.commonKey == .commonKeyTitle { - title = data.stringValue - } - } - if let artist, !artist.isEmpty { - nameComponents.append(artist) - } - if let title, !title.isEmpty { - nameComponents.append(title) - } - if nameComponents.isEmpty, var filename = file.fileName { - if let dotIndex = filename.lastIndex(of: ".") { - filename = String(filename[.. deliverOnMainQueue).startStrict(next: { [weak self] message in @@ -5785,7 +5727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - let themeEmoticon: Signal = self.chatThemeEmoticonPromise.get() + let chatTheme: Signal = self.chatThemePromise.get() |> distinctUntilChanged let uploadingChatWallpaper: Signal @@ -5821,18 +5763,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let accountManager = context.sharedContext.accountManager - let currentThemeEmoticon = Atomic<(String?, Bool)?>(value: nil) + let currentChatTheme = Atomic<(ChatTheme?, Bool)?>(value: nil) self.presentationDataDisposable = combineLatest( queue: Queue.mainQueue(), context.sharedContext.presentationData, themeSettings, context.engine.themes.getChatThemes(accountManager: accountManager, onlyCached: true), - themeEmoticon, - self.themeEmoticonAndDarkAppearancePreviewPromise.get(), + chatTheme, + self.chatThemeAndDarkAppearancePreviewPromise.get(), chatWallpaper - ).startStrict(next: { [weak self] presentationData, themeSettings, chatThemes, themeEmoticon, themeEmoticonAndDarkAppearance, chatWallpaper in + ).startStrict(next: { [weak self] presentationData, themeSettings, chatThemes, chatTheme, chatThemeAndDarkAppearance, chatWallpaper in if let strongSelf = self { - let (themeEmoticonPreview, darkAppearancePreview) = themeEmoticonAndDarkAppearance + let (chatThemePreview, darkAppearancePreview) = chatThemeAndDarkAppearance var chatWallpaper = chatWallpaper @@ -5840,19 +5782,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let previousStrings = strongSelf.presentationData.strings let previousChatWallpaper = strongSelf.presentationData.chatWallpaper - var themeEmoticon = themeEmoticon - if let themeEmoticonPreview = themeEmoticonPreview { - if !themeEmoticonPreview.isEmpty { - if themeEmoticon?.strippedEmoji != themeEmoticonPreview.strippedEmoji { + var chatTheme = chatTheme + if let chatThemePreview { + if !chatThemePreview.isEmpty { + if chatTheme?.id != chatThemePreview.id { chatWallpaper = nil - themeEmoticon = themeEmoticonPreview + chatTheme = chatThemePreview } } else { - themeEmoticon = nil + chatTheme = nil } } if strongSelf.chatLocation.peerId == strongSelf.context.account.peerId { - themeEmoticon = nil + chatTheme = nil } var presentationData = presentationData @@ -5872,17 +5814,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G chatWallpaper = themeWallpaper } } - if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoticon?.strippedEmoji == themeEmoticon.strippedEmoji }) { - if let darkAppearancePreview = darkAppearancePreview { - useDarkAppearance = darkAppearancePreview - } - if let theme = makePresentationTheme(cloudTheme: theme, dark: useDarkAppearance) { - theme.forceSync = true - presentationData = presentationData.withUpdated(theme: theme).withUpdated(chatWallpaper: theme.chat.defaultWallpaper) - - Queue.mainQueue().after(1.0, { - theme.forceSync = false - }) + if let chatTheme { + switch chatTheme { + case let .emoticon(emoticon): + if let theme = chatThemes.first(where: { $0.emoticon?.strippedEmoji == emoticon.strippedEmoji }) { + if let darkAppearancePreview = darkAppearancePreview { + useDarkAppearance = darkAppearancePreview + } + if let theme = makePresentationTheme(cloudTheme: theme, dark: useDarkAppearance) { + theme.forceSync = true + presentationData = presentationData.withUpdated(theme: theme).withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + + Queue.mainQueue().after(1.0, { + theme.forceSync = false + }) + } + } + case .gift: + if let theme = makePresentationTheme(chatTheme: chatTheme, dark: useDarkAppearance) { + theme.forceSync = true + presentationData = presentationData.withUpdated(theme: theme).withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + + Queue.mainQueue().after(1.0, { + theme.forceSync = false + }) + } } } else if let darkAppearancePreview = darkAppearancePreview { useDarkAppearance = darkAppearancePreview @@ -5977,7 +5933,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.presentationData = presentationData strongSelf.didSetPresentationData = true - let previousThemeEmoticon = currentThemeEmoticon.swap((themeEmoticon, useDarkAppearance)) + let previousChatTheme = currentChatTheme.swap((chatTheme, useDarkAppearance)) if isFirstTime || previousTheme != presentationData.theme || previousStrings !== presentationData.strings || presentationData.chatWallpaper != previousChatWallpaper { strongSelf.themeAndStringsUpdated() @@ -5985,7 +5941,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G controllerInteraction.updatedPresentationData = strongSelf.updatedPresentationData strongSelf.presentationDataPromise.set(.single(strongSelf.presentationData)) - if !isFirstTime && (previousThemeEmoticon?.0 != themeEmoticon || previousThemeEmoticon?.1 != useDarkAppearance) { + if !isFirstTime && (previousChatTheme?.0 != chatTheme || previousChatTheme?.1 != useDarkAppearance) { strongSelf.presentCrossfadeSnapshot() } } @@ -6179,6 +6135,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.updateChatLocationThreadDisposable?.dispose() self.accountPeerDisposable?.dispose() self.contentDataDisposable?.dispose() + self.newTopicEventsDisposable?.dispose() self.updateMessageTodoDisposables?.dispose() self.preloadNextChatPeerIdDisposable.dispose() } @@ -7305,9 +7262,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState) } interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) - /*if case .peer = self.chatLocation, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum { - interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()).withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) - }*/ let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in return interfaceState }).startStandalone() @@ -8090,10 +8044,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } func transformEnqueueMessages(_ messages: [EnqueueMessage], silentPosting: Bool, scheduleTime: Int32? = nil, postpone: Bool = false) -> [EnqueueMessage] { + var defaultThreadId: Int64? var defaultReplyMessageSubject: EngineMessageReplySubject? switch self.chatLocation { case .peer: - break + if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.linkedBotId != nil { + defaultThreadId = EngineMessage.newTopicThreadId + } case let .replyThread(replyThreadMessage): if let effectiveMessageId = replyThreadMessage.effectiveMessageId { defaultReplyMessageSubject = EngineMessageReplySubject(messageId: effectiveMessageId, quote: nil, todoItemId: nil) @@ -8118,6 +8075,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } } + if let defaultThreadId { + var updateThread = false + switch message { + case let .message(_, _, _, _, threadId, replyToMessageId, _, _, _, _): + if threadId == nil && replyToMessageId == nil { + updateThread = true + } + case let .forward(_, threadId, _, _, _): + if threadId == nil { + updateThread = true + } + } + if updateThread { + message = message.withUpdatedThreadId(defaultThreadId) + } + } if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { switch message { @@ -8822,15 +8795,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - let startingBot = self.startingBot - startingBot.set(true) - self.editMessageDisposable.set((self.context.engine.messages.requestStartBot(botPeerId: peerId, payload: payload) |> deliverOnMainQueue |> afterDisposed({ - startingBot.set(false) - })).startStrict(completed: { [weak self] in - if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedBotStartPayload(nil) }) + Task { @MainActor [weak self] in + guard let self, let navigationController = self.navigationController as? NavigationController else { + return } - })) + + #if DEBUG && false + let botForumId = await self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: peerId) + ).get() + if case let .known(botForumIdValue) = botForumId, let botForumIdValue { + let botForum = await self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: botForumIdValue) + ).get() + + if let botForum { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(botForum), keepStack: .always)) + } + } + #endif + + let startingBot = self.startingBot + startingBot.set(true) + self.editMessageDisposable.set((self.context.engine.messages.requestStartBot(botPeerId: peerId, payload: payload) |> deliverOnMainQueue |> afterDisposed({ + startingBot.set(false) + })).startStrict(completed: { [weak self] in + if let strongSelf = self { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedBotStartPayload(nil) }) + } + })) + } } func openResolved(result: ResolvedUrl, sourceMessageId: MessageId?, progress: Promise? = nil, forceExternal: Bool = false, concealed: Bool = false, commit: @escaping () -> Void = {}) { @@ -9780,12 +9774,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let _ = self.presentationInterfaceState.inputTextPanelState.mediaRecordingState { alertAction?() Queue.mainQueue().after(delay ? 0.2 : 0.0) { - self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Conversation_DiscardVoiceMessageDescription, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_DiscardVoiceMessageAction, action: { [weak self] in - self?.stopMediaRecorder() - Queue.mainQueue().after(0.1) { - action() - } - })]), in: .window(.root)) + let alertController = textAlertController( + context: self.context, + updatedPresentationData: self.updatedPresentationData, + title: nil, + text: self.presentationData.strings.Conversation_StopVoiceMessageDescription, + actions: [ + TextAlertAction( + type: .defaultAction, + title: self.presentationData.strings.Conversation_StopVoiceMessagePauseAction, + action: { [weak self] in + self?.stopMediaRecorder(pause: true) + Queue.mainQueue().after(0.1) { + action() + } + } + ), + TextAlertAction( + type: .genericAction, + title: self.presentationData.strings.Common_Cancel, + action: { + } + ), + ], + actionLayout: .vertical + ) + + self.present(alertController, in: .window(.root)) } return true @@ -10063,17 +10078,34 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var isUpdatingChatLocationThread: Bool = false var currentChatSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection? - public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) { + func updateChatLocationToOther(chatLocation: ChatLocation) { + self.saveInterfaceState() + self.chatDisplayNode.dismissTextInput() + + let updatedChatLocation: ChatLocation = chatLocation + let chatLocationContextHolder = Atomic(value: nil) + let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder) + self.isUpdatingChatLocationThread = true + self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, apply: { [weak self, weak historyNode] apply in + guard let self, let historyNode else { + return + } + + self.currentChatSwitchDirection = nil + self.chatLocation = updatedChatLocation + historyNode.areContentAnimationsEnabled = true + self.chatDisplayNode.prepareSwitchToChatLocation(chatLocation: chatLocation, historyNode: historyNode, animationDirection: nil) + + apply(self.didAppear ? .animated(duration: 0.4, curve: .spring) : .immediate) + + self.currentChatSwitchDirection = nil + self.isUpdatingChatLocationThread = false + }) + } + + func updateInitialChatBotForumLocationThread(linkedForumId: EnginePeer.Id, threadId: Int64) { if self.isUpdatingChatLocationThread { - return - } - guard let peerId = self.chatLocation.peerId else { - return - } - if self.chatLocation.threadId == threadId { - return - } - guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else { + assertionFailure() return } @@ -10081,34 +10113,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.dismissTextInput() - let updatedChatLocation: ChatLocation - if let threadId { - var isMonoforum = false - if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { - isMonoforum = true - } - - updatedChatLocation = .replyThread(message: ChatReplyThreadMessage( - peerId: peerId, - threadId: threadId, - channelMessageId: nil, - isChannelPost: false, - isForumPost: true, - isMonoforumPost: isMonoforum, - maxMessage: nil, - maxReadIncomingMessageId: nil, - maxReadOutgoingMessageId: nil, - unreadCount: 0, - initialFilledHoles: IndexSet(), - initialAnchor: .automatic, - isNotAvailable: false - )) - } else { - updatedChatLocation = .peer(id: peerId) - } - - let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() - let avatarSnapshot = self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil ? (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() : nil + let updatedChatLocation: ChatLocation = .replyThread(message: ChatReplyThreadMessage( + peerId: linkedForumId, + threadId: threadId, + channelMessageId: nil, + isChannelPost: false, + isForumPost: true, + isMonoforumPost: false, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )) let chatLocationContextHolder = Atomic(value: nil) let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder) @@ -10118,38 +10137,189 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - self.currentChatSwitchDirection = animationDirection + self.currentChatSwitchDirection = nil self.chatLocation = updatedChatLocation historyNode.areContentAnimationsEnabled = true - self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: animationDirection) + self.chatDisplayNode.prepareSwitchToChatLocation(chatLocation: updatedChatLocation, historyNode: historyNode, animationDirection: nil) - apply(true) - - if let navigationSnapshot, let animationDirection { - let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection - switch animationDirection { - case .up: - mappedAnimationDirection = .up - case .down: - mappedAnimationDirection = .down - case .left: - mappedAnimationDirection = .left - case .right: - mappedAnimationDirection = .right - } - - self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection) - } - - if let avatarSnapshot, self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil { - (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshot) - } + apply(.animated(duration: 0.4, curve: .spring)) self.currentChatSwitchDirection = nil self.isUpdatingChatLocationThread = false }) } + public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil, replaceInline: Bool = false, transferInputState: Bool = false, completion: (() -> Void)? = nil) { + Task { @MainActor [weak self] in + guard let self else { + completion?() + return + } + if self.isUpdatingChatLocationThread { + completion?() + return + } + guard let peerId = self.chatLocation.peerId else { + completion?() + return + } + if self.chatLocation.threadId == threadId { + completion?() + return + } + guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else { + completion?() + return + } + + var clearInputState = false + if transferInputState { + var peerId: PeerId + var currentThreadId: Int64? + switch self.chatLocation { + case let .peer(peerIdValue): + peerId = peerIdValue + case let .replyThread(replyThreadMessage): + peerId = replyThreadMessage.peerId + currentThreadId = replyThreadMessage.threadId + case .customChatContents: + return + } + + let timestamp = Int32(Date().timeIntervalSince1970) + var interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp) + let composeInputState = interfaceState.composeInputState + interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()) + interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil) + interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: currentThreadId, { _ in + return interfaceState + }).startStandalone() + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { current in + if currentThreadId != EngineMessage.newTopicThreadId { + return current.withUpdatedComposeInputState(composeInputState) + } else { + return current.withUpdatedComposeInputState(ChatTextInputState()) + } + }).startStandalone() + + if currentThreadId == EngineMessage.newTopicThreadId { + clearInputState = true + } + } else { + self.saveInterfaceState() + self.chatDisplayNode.dismissTextInput() + } + + let updatedChatLocation: ChatLocation + if let threadId { + if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) { + guard case let .known(linkedForumId) = await self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id) + ).get(), let linkedForumId else { + return + } + + updatedChatLocation = .replyThread(message: ChatReplyThreadMessage( + peerId: linkedForumId, + threadId: threadId, + channelMessageId: nil, + isChannelPost: false, + isForumPost: true, + isMonoforumPost: false, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )) + } else { + var isMonoforum = false + if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) { + isMonoforum = true + } + + updatedChatLocation = .replyThread(message: ChatReplyThreadMessage( + peerId: peerId, + threadId: threadId, + channelMessageId: nil, + isChannelPost: false, + isForumPost: true, + isMonoforumPost: isMonoforum, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )) + } + } else { + /*if let channel = peer as? TelegramChannel, let linkedBotId = channel.linkedBotId { + updatedChatLocation = .peer(id: linkedBotId) + } else {*/ + updatedChatLocation = .peer(id: peerId) + //} + } + + let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() + let avatarSnapshot = self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil ? (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() : nil + + let chatLocationContextHolder = Atomic(value: nil) + let historyNode = replaceInline ? self.chatDisplayNode.historyNode : self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder) + self.isUpdatingChatLocationThread = true + self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, apply: { [weak self, weak historyNode] apply in + guard let self, let historyNode else { + return + } + + self.currentChatSwitchDirection = animationDirection + self.chatLocation = updatedChatLocation + historyNode.areContentAnimationsEnabled = true + self.chatDisplayNode.prepareSwitchToChatLocation(chatLocation: updatedChatLocation, historyNode: historyNode, animationDirection: animationDirection) + + apply(.animated(duration: transferInputState ? 0.3 : 0.4, curve: .spring)) + + if let navigationSnapshot, let animationDirection { + let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection + switch animationDirection { + case .up: + mappedAnimationDirection = .up + case .down: + mappedAnimationDirection = .down + case .left: + mappedAnimationDirection = .left + case .right: + mappedAnimationDirection = .right + } + + self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection) + } + + if let avatarSnapshot, self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil { + (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshot) + } + + self.currentChatSwitchDirection = nil + self.isUpdatingChatLocationThread = false + + if clearInputState { + //DispatchQueue.main.async { [weak self] in + // guard let self else { + // return + // } + self.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInterfaceState { $0.withUpdatedComposeInputState(ChatTextInputState()) } }) + //} + } + + completion?() + }) + } + } + public var contentContainerNode: ASDisplayNode { return self.chatDisplayNode.contentContainerNode } diff --git a/submodules/TelegramUI/Sources/ChatControllerContentData.swift b/submodules/TelegramUI/Sources/ChatControllerContentData.swift index 5fc625bacd..0fa02f6527 100644 --- a/submodules/TelegramUI/Sources/ChatControllerContentData.swift +++ b/submodules/TelegramUI/Sources/ChatControllerContentData.swift @@ -81,6 +81,23 @@ extension ChatControllerImpl { } } + struct HistoryNodeData: Equatable { + let chatLocation: ChatLocation + let chatLocationContextHolder: Atomic + + init(chatLocation: ChatLocation, chatLocationContextHolder: Atomic) { + self.chatLocation = chatLocation + self.chatLocationContextHolder = chatLocationContextHolder + } + + static func ==(lhs: HistoryNodeData, rhs: HistoryNodeData) -> Bool { + if lhs.chatLocation != rhs.chatLocation { + return false + } + return true + } + } + struct State { var peerView: PeerView? var threadInfo: EngineMessageHistoryThread.Info? @@ -144,6 +161,8 @@ extension ChatControllerImpl { var removePaidMessageFeeData: ChatPresentationInterfaceState.RemovePaidMessageFeeData? var preloadNextChatPeerId: EnginePeer.Id? + + var historyNodeData: HistoryNodeData? } private let presentationData: PresentationData @@ -199,7 +218,7 @@ extension ChatControllerImpl { var historyNavigationStack = ChatHistoryNavigationStack() - let chatThemeEmoticonPromise = Promise() + let chatThemePromise = Promise() let chatWallpaperPromise = Promise() private(set) var inviteRequestsContext: PeerInvitationImportersContext? @@ -559,6 +578,16 @@ extension ChatControllerImpl { } else { strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, true) } + } else if let channel = peer as? TelegramChannel, let linkedBotId = channel.linkedBotId, let mainPeer = peerView.peers[linkedBotId] { + strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData( + peerId: mainPeer.id, + peer: mainPeer, + isContact: false, + isSavedMessages: false, + notificationSettings: nil, + peerPresences: [:], + cachedData: nil + ), customTitle: nil, customSubtitle: nil, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: nil, customMessageCount: nil, isEnabled: true) } else { strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) @@ -806,6 +835,13 @@ extension ChatControllerImpl { } disallowedGifts = cachedData.disallowedGifts } + + if chatLocation.threadId == nil, case let .known(value) = cachedData.linkedBotChannelId, let value, chatLocation.peerId != value { + strongSelf.state.historyNodeData = HistoryNodeData( + chatLocation: .peer(id: value), + chatLocationContextHolder: Atomic(value: nil) + ) + } } else if let cachedData = peerView.cachedData as? CachedGroupData { var invitedBy: Peer? if let invitedByPeerId = cachedData.invitedBy { @@ -1425,12 +1461,19 @@ extension ChatControllerImpl { ) var customMessageCount: Int? - if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.isMonoForum { + var customSubtitle: String? + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel { + if peer.isMonoForum { + } else if peer.linkedBotId != nil { + customSubtitle = strongSelf.presentationData.strings.Bot_GenericBotStatus + } else { + customMessageCount = savedMessagesPeer?.messageCount ?? 0 + } } else { customMessageCount = savedMessagesPeer?.messageCount ?? 0 } - strongSelf.state.chatTitleContent = .peer(peerView: mappedPeerData, customTitle: nil, customSubtitle: nil, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: false, customMessageCount: customMessageCount, isEnabled: true) + strongSelf.state.chatTitleContent = .peer(peerView: mappedPeerData, customTitle: nil, customSubtitle: customSubtitle, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: false, customMessageCount: customMessageCount, isEnabled: true) strongSelf.state.peerView = peerView @@ -1517,7 +1560,15 @@ extension ChatControllerImpl { } if let threadInfo = messageAndTopic.threadData?.info { - strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true) + var customSubtitle: String? + if messageAndTopic.messageCount == 0, let peer = peerView.peers[peerView.peerId] as? TelegramChannel { + if peer.linkedBotId != nil { + //TODO:localize + customSubtitle = "topic" + } + } + + strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, customSubtitle: customSubtitle, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true) let avatarContent: EmojiStatusComponent.Content if chatLocation.threadId == 1 { @@ -1535,6 +1586,10 @@ extension ChatControllerImpl { infoContextActionIsEnabled = true } strongSelf.state.infoAvatar = .emojiStatus(content: avatarContent, contextActionIsEnabled: infoContextActionIsEnabled) + } else if chatLocation.threadId == EngineMessage.newTopicThreadId { + //TODO:localize + strongSelf.state.chatTitleContent = .custom("New Chat", nil, false) + strongSelf.state.infoAvatar = nil } else { strongSelf.state.chatTitleContent = .replyThread(type: replyThreadType, count: count) } @@ -2150,8 +2205,23 @@ extension ChatControllerImpl { topPinnedMessage = ChatControllerImpl.topPinnedScrollMessage(context: context, chatLocation: chatLocation, historyNode: historyNode, scrolledToMessageId: self.scrolledToMessageId.get()) } + let cachedData: Signal<(CachedPeerData?, [MessageId: Message]), NoError> + if let peerId = chatLocation.peerId { + cachedData = context.account.postbox.combinedView(keys: [ + .cachedPeerDataWithMessages(peerId: peerId) + ]) |> map { views -> (CachedPeerData?, [MessageId: Message]) in + guard let view = views.views[.cachedPeerDataWithMessages(peerId: peerId)] as? CachedPeerDataView else { + return (nil, [:]) + } + return (view.cachedPeerData, view.associatedMessages) + } + } else { + cachedData = .single((nil, [:])) + } + self.cachedDataDisposable?.dispose() - self.cachedDataDisposable = combineLatest(queue: .mainQueue(), historyNode.cachedPeerDataAndMessages, + self.cachedDataDisposable = combineLatest(queue: .mainQueue(), + cachedData, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, @@ -2170,19 +2240,19 @@ extension ChatControllerImpl { let (cachedData, messages) = cachedDataAndMessages if cachedData != nil { - var themeEmoticon: String? = nil + var chatTheme: ChatTheme? = nil var chatWallpaper: TelegramWallpaper? if let cachedData = cachedData as? CachedUserData { - themeEmoticon = cachedData.themeEmoticon + chatTheme = cachedData.chatTheme chatWallpaper = cachedData.wallpaper } else if let cachedData = cachedData as? CachedGroupData { - themeEmoticon = cachedData.themeEmoticon + chatTheme = cachedData.chatTheme } else if let cachedData = cachedData as? CachedChannelData { - themeEmoticon = cachedData.themeEmoticon + chatTheme = cachedData.chatTheme chatWallpaper = cachedData.wallpaper } - strongSelf.chatThemeEmoticonPromise.set(.single(themeEmoticon)) + strongSelf.chatThemePromise.set(.single(chatTheme)) strongSelf.chatWallpaperPromise.set(.single(chatWallpaper)) } @@ -2237,7 +2307,7 @@ extension ChatControllerImpl { pinnedMessageId = replyThreadMessage.effectiveTopId } if let pinnedMessageId = pinnedMessageId { - if let message = messages?[pinnedMessageId] { + if let message = messages[pinnedMessageId] { pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id) } } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index a434dbe01c..4666344780 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -46,6 +46,7 @@ import ComponentFlow import ChatEmptyNode import SpaceWarpView import ChatSideTopicsPanel +import ChatThemeScreen final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem { let itemNode: OverlayMediaItemNode @@ -553,7 +554,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return (messages, Int32(messages.count), false) } - source = .custom(messages: messages, messageId: MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: nil, loadMore: nil) + source = .custom(messages: messages, messageId: MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: nil, isSavedMusic: false, canReorder: false, loadMore: nil) case let .reply(reply): let messages = combineLatest(context.account.postbox.messagesAtIds(messageIds), context.account.postbox.loadedPeerWithId(context.account.peerId)) |> map { messages, accountPeer -> ([Message], Int32, Bool) in @@ -567,7 +568,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return (messages, Int32(messages.count), false) } - source = .custom(messages: messages, messageId: messageIds.first ?? MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: reply.quote.flatMap { quote in ChatHistoryListSource.Quote(text: quote.text, offset: quote.offset) }, loadMore: nil) + source = .custom(messages: messages, messageId: messageIds.first ?? MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: reply.quote.flatMap { quote in ChatHistoryListSource.Quote(text: quote.text, offset: quote.offset) }, isSavedMusic: false, canReorder: false, loadMore: nil) case let .link(link): let messages = link.options |> mapToSignal { options -> Signal<(ChatControllerSubject.LinkOptions, Peer, Message?, [StoryId: CodableEntry]), NoError> in @@ -668,13 +669,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return ([message], 1, false) } - source = .custom(messages: messages, messageId: MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: nil, loadMore: nil) + source = .custom(messages: messages, messageId: MessageId(peerId: PeerId(0), namespace: 0, id: 0), quote: nil, isSavedMusic: false, canReorder: false, loadMore: nil) } } else if case .customChatContents = chatLocation { if case let .customChatContents(customChatContents) = subject { source = .customView(historyView: customChatContents.historyView) } else { - source = .custom(messages: .single(([], 0, false)), messageId: nil, quote: nil, loadMore: nil) + source = .custom(messages: .single(([], 0, false)), messageId: nil, quote: nil, isSavedMusic: false, canReorder: false, loadMore: nil) } } else { source = .default @@ -2034,7 +2035,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { previousHistoryNode?.removeFromSupernode() }) - transition.animatePosition(layer: self.messageTransitionNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) + //transition.animatePosition(layer: self.messageTransitionNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) transition.animatePosition(layer: previousMessageTransitionNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousMessageTransitionNode] _ in previousMessageTransitionNode?.removeFromSupernode() }) @@ -3582,7 +3583,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { let themeUpdated = presentationReadyUpdated || (self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme) - self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper, animated: true) + self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper, starGift: chatPresentationInterfaceState.theme.starGift, animated: true) self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8) if self.pendingSwitchToChatLocation == nil { @@ -4464,282 +4465,296 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } func sendCurrentMessage(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, postpone: Bool = false, messageEffect: ChatSendMessageEffect? = nil, completion: @escaping () -> Void = {}) { - if let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { - self.historyNode.justSentTextMessage = true - - if let textInputNode = textInputPanelNode.textInputNode, textInputNode.isFirstResponder() { - Keyboard.applyAutocorrection(textView: textInputNode.textView) + + guard let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode else { + return + } + self.historyNode.justSentTextMessage = true + + if let textInputNode = textInputPanelNode.textInputNode, textInputNode.isFirstResponder() { + Keyboard.applyAutocorrection(textView: textInputNode.textView) + } + + var effectivePresentationInterfaceState = self.chatPresentationInterfaceState + + if let textInputPanelNode = self.textInputPanelNode { + effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) } + } + + if let _ = effectivePresentationInterfaceState.interfaceState.editMessage, effectivePresentationInterfaceState.interfaceState.postSuggestionState == nil { + self.interfaceInteraction?.editMessage() + } else { + var isScheduledMessages = false + if case .scheduledMessages = effectivePresentationInterfaceState.subject { + isScheduledMessages = true } - var effectivePresentationInterfaceState = self.chatPresentationInterfaceState - - if let textInputPanelNode = self.textInputPanelNode { - effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) } + if let _ = effectivePresentationInterfaceState.slowmodeState, !isScheduledMessages && scheduleTime == nil { + if let rect = self.frameForInputActionButton() { + self.interfaceInteraction?.displaySlowmodeTooltip(self.view, rect) + } + return } - if let _ = effectivePresentationInterfaceState.interfaceState.editMessage, effectivePresentationInterfaceState.interfaceState.postSuggestionState == nil { - self.interfaceInteraction?.editMessage() + var messages: [EnqueueMessage] = [] + + let effectiveInputText: NSAttributedString + + if effectivePresentationInterfaceState.interfaceState.editMessage != nil && effectivePresentationInterfaceState.interfaceState.postSuggestionState != nil { + effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.effectiveInputState.inputText) } else { - var isScheduledMessages = false - if case .scheduledMessages = effectivePresentationInterfaceState.subject { - isScheduledMessages = true - } - - if let _ = effectivePresentationInterfaceState.slowmodeState, !isScheduledMessages && scheduleTime == nil { - if let rect = self.frameForInputActionButton() { - self.interfaceInteraction?.displaySlowmodeTooltip(self.view, rect) - } - return - } - - var messages: [EnqueueMessage] = [] - - let effectiveInputText: NSAttributedString - - if effectivePresentationInterfaceState.interfaceState.editMessage != nil && effectivePresentationInterfaceState.interfaceState.postSuggestionState != nil { - effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.effectiveInputState.inputText) - } else { - effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText) - } - - let peerSpecificEmojiPack = (self.controller?.contentData?.state.peerView?.cachedData as? CachedChannelData)?.emojiPack - - var inlineStickers: [MediaId: Media] = [:] - var firstLockedPremiumEmoji: TelegramMediaFile? - var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:] - effectiveInputText.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: effectiveInputText.length), using: { value, _, _ in - if let value = value as? ChatTextInputTextCustomEmojiAttribute { - if let file = value.file { - inlineStickers[file.fileId] = file - if let packId = value.interactivelySelectedFromPackId { - bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId - } - - var isPeerSpecific = false - for attribute in file.attributes { - if case let .CustomEmoji(_, _, _, packReference) = attribute, case let .id(id, _) = packReference { - isPeerSpecific = id == peerSpecificEmojiPack?.id.id - } - } - - if file.isPremiumEmoji && !self.chatPresentationInterfaceState.isPremium && self.chatPresentationInterfaceState.chatLocation.peerId != self.context.account.peerId && !isPeerSpecific { - if firstLockedPremiumEmoji == nil { - firstLockedPremiumEmoji = file - } - } + effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText) + } + + let peerSpecificEmojiPack = (self.controller?.contentData?.state.peerView?.cachedData as? CachedChannelData)?.emojiPack + + var inlineStickers: [MediaId: Media] = [:] + var firstLockedPremiumEmoji: TelegramMediaFile? + var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:] + effectiveInputText.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: effectiveInputText.length), using: { value, _, _ in + if let value = value as? ChatTextInputTextCustomEmojiAttribute { + if let file = value.file { + inlineStickers[file.fileId] = file + if let packId = value.interactivelySelectedFromPackId { + bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId } - } - }) - - if let firstLockedPremiumEmoji = firstLockedPremiumEmoji { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - self.controllerInteraction.displayUndo(.sticker(context: context, file: firstLockedPremiumEmoji, loop: true, title: nil, text: presentationData.strings.EmojiInput_PremiumEmojiToast_Text, undoText: presentationData.strings.EmojiInput_PremiumEmojiToast_Action, customAction: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.dismissTextInput() - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumDemoScreen(context: strongSelf.context, subject: .animatedEmoji, action: { - let controller = PremiumIntroScreen(context: strongSelf.context, source: .animatedEmoji) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + var isPeerSpecific = false + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute, case let .id(id, _) = packReference { + isPeerSpecific = id == peerSpecificEmojiPack?.id.id + } } - strongSelf.controller?.present(controller, in: .window(.root), with: nil) - })) - - return + + if file.isPremiumEmoji && !self.chatPresentationInterfaceState.isPremium && self.chatPresentationInterfaceState.chatLocation.peerId != self.context.account.peerId && !isPeerSpecific { + if firstLockedPremiumEmoji == nil { + firstLockedPremiumEmoji = file + } + } + } } + }) + + if let firstLockedPremiumEmoji = firstLockedPremiumEmoji { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + self.controllerInteraction.displayUndo(.sticker(context: context, file: firstLockedPremiumEmoji, loop: true, title: nil, text: presentationData.strings.EmojiInput_PremiumEmojiToast_Text, undoText: presentationData.strings.EmojiInput_PremiumEmojiToast_Action, customAction: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.dismissTextInput() + + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: strongSelf.context, subject: .animatedEmoji, action: { + let controller = PremiumIntroScreen(context: strongSelf.context, source: .animatedEmoji) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + strongSelf.controller?.present(controller, in: .window(.root), with: nil) + })) - if let replyMessageSubject = self.chatPresentationInterfaceState.interfaceState.replyMessageSubject, let quote = replyMessageSubject.quote { - if let replyMessage = self.chatPresentationInterfaceState.replyMessage { - let nsText = replyMessage.text as NSString - var startIndex = 0 - var found = false - while true { - let range = nsText.range(of: quote.text, range: NSRange(location: startIndex, length: nsText.length - startIndex)) - if range.location != NSNotFound { - let subEntities = messageTextEntitiesInRange(entities: replyMessage.textEntitiesAttribute?.entities ?? [], range: range, onlyQuoteable: true) - if subEntities == quote.entities { - found = true - break - } - - startIndex = range.upperBound - } else { + return + } + + if let replyMessageSubject = self.chatPresentationInterfaceState.interfaceState.replyMessageSubject, let quote = replyMessageSubject.quote { + if let replyMessage = self.chatPresentationInterfaceState.replyMessage { + let nsText = replyMessage.text as NSString + var startIndex = 0 + var found = false + while true { + let range = nsText.range(of: quote.text, range: NSRange(location: startIndex, length: nsText.length - startIndex)) + if range.location != NSNotFound { + let subEntities = messageTextEntitiesInRange(entities: replyMessage.textEntitiesAttribute?.entities ?? [], range: range, onlyQuoteable: true) + if subEntities == quote.entities { + found = true break } + + startIndex = range.upperBound + } else { + break } + } + + if !found { + let authorName: String = (replyMessage.author.flatMap(EnginePeer.init))?.compactDisplayTitle ?? "" + let errorTextData = self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedText(authorName) + let errorText = errorTextData.string + self.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.context.sharedContext.currentPresentationData.with({ $0 })), title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedTitle, text: errorText, actions: [ + TextAlertAction(type: .genericAction, title: self.chatPresentationInterfaceState.strings.Common_Cancel, action: {}), + TextAlertAction(type: .defaultAction, title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedActionEdit, action: { [weak self] in + guard let self, let controller = self.controller else { + return + } + controller.updateChatPresentationInterfaceState(interactive: false, { presentationInterfaceState in + return presentationInterfaceState.updatedInterfaceState { interfaceState in + guard var replyMessageSubject = interfaceState.replyMessageSubject else { + return interfaceState + } + replyMessageSubject.quote = nil + return interfaceState.withUpdatedReplyMessageSubject(replyMessageSubject) + } + }) + presentChatLinkOptions(selfController: controller, sourceNode: controller.displayNode) + }), + ], parseMarkdown: true), in: .window(.root)) - if !found { - let authorName: String = (replyMessage.author.flatMap(EnginePeer.init))?.compactDisplayTitle ?? "" - let errorTextData = self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedText(authorName) - let errorText = errorTextData.string - self.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.context.sharedContext.currentPresentationData.with({ $0 })), title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedTitle, text: errorText, actions: [ - TextAlertAction(type: .genericAction, title: self.chatPresentationInterfaceState.strings.Common_Cancel, action: {}), - TextAlertAction(type: .defaultAction, title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedActionEdit, action: { [weak self] in - guard let self, let controller = self.controller else { - return - } - controller.updateChatPresentationInterfaceState(interactive: false, { presentationInterfaceState in - return presentationInterfaceState.updatedInterfaceState { interfaceState in - guard var replyMessageSubject = interfaceState.replyMessageSubject else { - return interfaceState - } - replyMessageSubject.quote = nil - return interfaceState.withUpdatedReplyMessageSubject(replyMessageSubject) - } - }) - presentChatLinkOptions(selfController: controller, sourceNode: controller.displayNode) - }), - ], parseMarkdown: true), in: .window(.root)) - - return - } + return } } + } + + let timestamp = CACurrentMediaTime() + if self.lastSendTimestamp + 0.15 > timestamp { + return + } + self.lastSendTimestamp = timestamp + + self.updateTypingActivity(false) + + let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines) + let peerId = effectivePresentationInterfaceState.chatLocation.peerId + if peerId?.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText), effectiveInputText.attribute(ChatTextInputAttributes.customEmoji, at: 0, effectiveRange: nil) == nil { + messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } else { + let inputText = convertMarkdownToAttributes(effectiveInputText) - let timestamp = CACurrentMediaTime() - if self.lastSendTimestamp + 0.15 > timestamp { - return - } - self.lastSendTimestamp = timestamp - - self.updateTypingActivity(false) - - let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines) - let peerId = effectivePresentationInterfaceState.chatLocation.peerId - if peerId?.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText), effectiveInputText.attribute(ChatTextInputAttributes.customEmoji, at: 0, effectiveRange: nil) == nil { - messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } else { - let inputText = convertMarkdownToAttributes(effectiveInputText) - - var mediaReference: AnyMediaReference? - var webpage: TelegramMediaWebpage? - if let urlPreview = self.chatPresentationInterfaceState.urlPreview { - if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) { - } else { - webpage = urlPreview.webPage - } + var mediaReference: AnyMediaReference? + var webpage: TelegramMediaWebpage? + if let urlPreview = self.chatPresentationInterfaceState.urlPreview { + if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) { + } else { + webpage = urlPreview.webPage } - mediaReference = webpage.flatMap(AnyMediaReference.standalone) - - if let postSuggestionState = effectivePresentationInterfaceState.interfaceState.postSuggestionState, let editingOriginalMessageId = postSuggestionState.editingOriginalMessageId { - if let editMessageState = effectivePresentationInterfaceState.editMessageState, let mediaReferenceValue = editMessageState.mediaReference { - mediaReference = mediaReferenceValue - } else { - if let message = self.historyNode.messageInCurrentHistoryView(editingOriginalMessageId) { - for media in message.media { - if media is TelegramMediaFile || media is TelegramMediaImage { - mediaReference = .message(message: MessageReference(message), media: media) - } + } + mediaReference = webpage.flatMap(AnyMediaReference.standalone) + + if let postSuggestionState = effectivePresentationInterfaceState.interfaceState.postSuggestionState, let editingOriginalMessageId = postSuggestionState.editingOriginalMessageId { + if let editMessageState = effectivePresentationInterfaceState.editMessageState, let mediaReferenceValue = editMessageState.mediaReference { + mediaReference = mediaReferenceValue + } else { + if let message = self.historyNode.messageInCurrentHistoryView(editingOriginalMessageId) { + for media in message.media { + if media is TelegramMediaFile || media is TelegramMediaImage { + mediaReference = .message(message: MessageReference(message), media: media) } } } } - - for text in breakChatInputText(trimChatInputText(inputText)) { - if text.length != 0 { - var attributes: [MessageAttribute] = [] - let entities: [MessageTextEntity] - if case let .customChatContents(customChatContents) = self.chatPresentationInterfaceState.subject, case .businessLinkSetup = customChatContents.kind { - entities = generateChatInputTextEntities(text, generateLinks: false) - } else { - entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text, maxAnimatedEmojisInText: 0)) - } - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - - if let urlPreview = self.chatPresentationInterfaceState.urlPreview { - if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) { - attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews])) - } else { - attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true, isSafe: false)) - } - } - - var bubbleUpEmojiOrStickersets: [ItemCollectionId] = [] - for entity in entities { - if case let .CustomEmoji(_, fileId) = entity.type { - if let packId = bubbleUpEmojiOrStickersetsById[fileId] { - if !bubbleUpEmojiOrStickersets.contains(packId) { - bubbleUpEmojiOrStickersets.append(packId) - } - } - } - } - - if bubbleUpEmojiOrStickersets.count > 1 { - bubbleUpEmojiOrStickersets.removeAll() - } - - messages.append(.message(text: text.string, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)) - mediaReference = nil - } - } - - if let mediaReferenceValue = mediaReference { - mediaReference = nil - messages.append(.message(text: "", attributes: [], inlineStickers: inlineStickers, mediaReference: mediaReferenceValue, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - - var forwardingToSameChat = false - if case let .peer(id) = self.chatPresentationInterfaceState.chatLocation, id.namespace == Namespaces.Peer.CloudUser, id != self.context.account.peerId, let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds, forwardMessageIds.count == 1 { - for messageId in forwardMessageIds { - if messageId.peerId == id { - forwardingToSameChat = true - } - } - } - if !messages.isEmpty && forwardingToSameChat { - self.controllerInteraction.displaySwipeToReplyHint() - } } - var postEmptyMessages = false - if case let .customChatContents(customChatContents) = self.chatPresentationInterfaceState.subject { - switch customChatContents.kind { - case .hashTagSearch: - break - case .quickReplyMessageInput: - break - case .businessLinkSetup: - postEmptyMessages = true - } - } - - if !messages.isEmpty, let messageEffect { - messages[0] = messages[0].withUpdatedAttributes { attributes in - var attributes = attributes - attributes.append(EffectMessageAttribute(id: messageEffect.id)) - return attributes - } - } - - if !messages.isEmpty || postEmptyMessages || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil { - if let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds { + for text in breakChatInputText(trimChatInputText(inputText)) { + if text.length != 0 { var attributes: [MessageAttribute] = [] - attributes.append(ForwardOptionsMessageAttribute(hideNames: self.chatPresentationInterfaceState.interfaceState.forwardOptionsState?.hideNames == true, hideCaptions: self.chatPresentationInterfaceState.interfaceState.forwardOptionsState?.hideCaptions == true)) - - var replyThreadId: Int64? - if case let .replyThread(replyThreadMessage) = self.chatPresentationInterfaceState.chatLocation { - replyThreadId = replyThreadMessage.threadId + let entities: [MessageTextEntity] + if case let .customChatContents(customChatContents) = self.chatPresentationInterfaceState.subject, case .businessLinkSetup = customChatContents.kind { + entities = generateChatInputTextEntities(text, generateLinks: false) + } else { + entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text, maxAnimatedEmojisInText: 0)) + } + if !entities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: entities)) + } + + if let urlPreview = self.chatPresentationInterfaceState.urlPreview { + if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) { + attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews])) + } else { + attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true, isSafe: false)) + } } - for id in forwardMessageIds.sorted() { - messages.append(.forward(source: id, threadId: replyThreadId, grouping: .auto, attributes: attributes, correlationId: nil)) + var bubbleUpEmojiOrStickersets: [ItemCollectionId] = [] + for entity in entities { + if case let .CustomEmoji(_, fileId) = entity.type { + if let packId = bubbleUpEmojiOrStickersetsById[fileId] { + if !bubbleUpEmojiOrStickersets.contains(packId) { + bubbleUpEmojiOrStickersets.append(packId) + } + } + } + } + + if bubbleUpEmojiOrStickersets.count > 1 { + bubbleUpEmojiOrStickersets.removeAll() + } + + messages.append(.message(text: text.string, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)) + mediaReference = nil + } + } + + if let mediaReferenceValue = mediaReference { + mediaReference = nil + messages.append(.message(text: "", attributes: [], inlineStickers: inlineStickers, mediaReference: mediaReferenceValue, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + + var forwardingToSameChat = false + if case let .peer(id) = self.chatPresentationInterfaceState.chatLocation, id.namespace == Namespaces.Peer.CloudUser, id != self.context.account.peerId, let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds, forwardMessageIds.count == 1 { + for messageId in forwardMessageIds { + if messageId.peerId == id { + forwardingToSameChat = true } } - - var usedCorrelationId: Int64? + } + if !messages.isEmpty && forwardingToSameChat { + self.controllerInteraction.displaySwipeToReplyHint() + } + } + + var postEmptyMessages = false + if case let .customChatContents(customChatContents) = self.chatPresentationInterfaceState.subject { + switch customChatContents.kind { + case .hashTagSearch: + break + case .quickReplyMessageInput: + break + case .businessLinkSetup: + postEmptyMessages = true + } + } + + if !messages.isEmpty, let messageEffect { + messages[0] = messages[0].withUpdatedAttributes { attributes in + var attributes = attributes + attributes.append(EffectMessageAttribute(id: messageEffect.id)) + return attributes + } + } + + if !messages.isEmpty || postEmptyMessages || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil { + if let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds { + var attributes: [MessageAttribute] = [] + attributes.append(ForwardOptionsMessageAttribute(hideNames: self.chatPresentationInterfaceState.interfaceState.forwardOptionsState?.hideNames == true, hideCaptions: self.chatPresentationInterfaceState.interfaceState.forwardOptionsState?.hideCaptions == true)) + var replyThreadId: Int64? + if case let .replyThread(replyThreadMessage) = self.chatPresentationInterfaceState.chatLocation { + replyThreadId = replyThreadMessage.threadId + } + + for id in forwardMessageIds.sorted() { + messages.append(.forward(source: id, threadId: replyThreadId, grouping: .auto, attributes: attributes, correlationId: nil)) + } + } + + let doSend: (Int64?) -> Void = { [weak self] overrideThreadId in + guard let self else { + return + } + + var messages = messages + if let overrideThreadId { + messages = messages.map { message in + return message.withUpdatedThreadId(overrideThreadId) + } + } + + var usedCorrelationId: Int64? if !messages.isEmpty, case .message = messages[messages.count - 1] { let correlationId = Int64.random(in: 0 ..< Int64.max) messages[messages.count - 1] = messages[messages.count - 1].withUpdatedCorrelationId(correlationId) - + var replyPanel: ReplyAccessoryPanelNode? if let accessoryPanelNode = self.accessoryPanelNode as? ReplyAccessoryPanelNode { replyPanel = accessoryPanelNode @@ -4751,35 +4766,67 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { }) } } - + self.setupSendActionOnViewUpdate({ [weak self] in - if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode { - strongSelf.collapseInput() - - strongSelf.ignoreUpdateHeight = true - textInputPanelNode.text = "" - strongSelf.requestUpdateChatInterfaceState(.immediate, true, { state in - var state = state - state = state.withUpdatedReplyMessageSubject(nil) - state = state.withUpdatedSendMessageEffect(nil) - - if state.postSuggestionState != nil { - state = state.withUpdatedPostSuggestionState(nil) - state = state.withUpdatedEditMessage(nil) - } - - state = state.withUpdatedForwardMessageIds(nil) - state = state.withUpdatedForwardOptionsState(nil) - state = state.withUpdatedComposeDisableUrlPreviews([]) - return state - }) - strongSelf.ignoreUpdateHeight = false + guard let self, let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode else { + return } + self.collapseInput() + + self.ignoreUpdateHeight = true + textInputPanelNode.text = "" + self.requestUpdateChatInterfaceState(.immediate, overrideThreadId == nil, { state in + var state = state + state = state.withUpdatedReplyMessageSubject(nil) + state = state.withUpdatedSendMessageEffect(nil) + + if state.postSuggestionState != nil { + state = state.withUpdatedPostSuggestionState(nil) + state = state.withUpdatedEditMessage(nil) + } + + state = state.withUpdatedForwardMessageIds(nil) + state = state.withUpdatedForwardOptionsState(nil) + state = state.withUpdatedComposeDisableUrlPreviews([]) + return state + }) + self.ignoreUpdateHeight = false }, usedCorrelationId) completion() self.sendMessages(messages, silentPosting, scheduleTime, messages.count > 1, postpone) } + + var targetThreadId: Int64? + if self.chatLocation.threadId == nil, let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.linkedBotId != nil { + if let message = messages.first { + switch message { + case let .message(_, _, _, _, _, replyToMessageId, _, _, _, _): + if let _ = replyToMessageId { + if let replyMessage = self.chatPresentationInterfaceState.replyMessage { + targetThreadId = replyMessage.threadId + } + } else { + targetThreadId = EngineMessage.newTopicThreadId + } + case let .forward(_, threadId, _, _, _): + targetThreadId = threadId + } + } + } + + if let targetThreadId { + self.historyNode.stopHistoryUpdates() + self.controller?.updateChatLocationThread(threadId: targetThreadId, animationDirection: .right, transferInputState: true, completion: { [weak self] in + guard let self else { + return + } + let _ = self + doSend(targetThreadId) + }) + } else { + doSend(nil) + } } } } @@ -5167,6 +5214,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } self.historyNode.enableExtractedBackgrounds = true + let chatLocation = self.chatLocation self.historyNode.setLoadStateUpdated { [weak self] loadState, animated in guard let strongSelf = self else { return @@ -5194,6 +5242,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } else if case .messages = loadState { strongSelf.didDisplayEmptyGreeting = true } + if chatLocation.threadId == EngineMessage.newTopicThreadId { + emptyType = nil + } strongSelf.updateIsEmpty(emptyType, wasLoading: wasLoading, animated: animated) } @@ -5286,109 +5337,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return historyNode } - func prepareSwitchToChatLocation(historyNode: ChatHistoryListNodeImpl, animationDirection: ChatControllerAnimateInnerChatSwitchDirection?) { + func prepareSwitchToChatLocation(chatLocation: ChatLocation, historyNode: ChatHistoryListNodeImpl, animationDirection: ChatControllerAnimateInnerChatSwitchDirection?) { self.chatLocation = historyNode.chatLocation - self.pendingSwitchToChatLocation = PendingSwitchToChatLocation( - historyNode: historyNode, - animationDirection: animationDirection - ) - } - - func updateChatLocation(chatLocation: ChatLocation, transition: ContainedViewLayoutTransition, tabSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?) { - if chatLocation == self.chatLocation { - return - } - self.chatLocation = chatLocation - - self.chatLocationContextHolder = Atomic(value: nil) - let historyNode = ChatHistoryListNodeImpl( - context: self.context, - updatedPresentationData: self.controller?.updatedPresentationData ?? (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), - chatLocation: chatLocation, - chatLocationContextHolder: self.chatLocationContextHolder, - adMessagesContext: self.adMessagesContext, - tag: nil, - source: .default, - subject: nil, - controllerInteraction: self.controllerInteraction, - selectedMessages: self.selectedMessagesPromise.get(), - rotated: self.controllerInteraction.chatIsRotated, - isChatPreview: false, - messageTransitionNode: { [weak self] in - return self?.messageTransitionNode - } - ) - - var getContentAreaInScreenSpaceImpl: (() -> CGRect)? - var onTransitionEventImpl: ((ContainedViewLayoutTransition) -> Void)? - let messageTransitionNode = ChatMessageTransitionNodeImpl(listNode: historyNode, getContentAreaInScreenSpace: { - return getContentAreaInScreenSpaceImpl?() ?? CGRect() - }, onTransitionEvent: { transition in - onTransitionEventImpl?(transition) - }) - - getContentAreaInScreenSpaceImpl = { [weak self] in - guard let strongSelf = self else { - return CGRect() - } - - return strongSelf.view.convert(strongSelf.frameForVisibleArea(), to: nil) - } - - onTransitionEventImpl = { [weak self] transition in - guard let strongSelf = self else { - return - } - if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion { - return - } - if strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency { - strongSelf.backgroundNode.animateEvent(transition: transition, extendAnimation: false) - } - } - - self.wrappingNode.contentNode.insertSubnode(messageTransitionNode, aboveSubnode: self.messageTransitionNode) - self.messageTransitionNode.removeFromSupernode() - self.messageTransitionNode = messageTransitionNode - - let previousHistoryNode = self.historyNode - previousHistoryNode.supernode?.insertSubnode(historyNode, aboveSubnode: previousHistoryNode) - self.historyNode = historyNode - - self.setupHistoryNode() - - historyNode.position = previousHistoryNode.position - historyNode.bounds = previousHistoryNode.bounds - historyNode.transform = previousHistoryNode.transform - - if let currentListViewLayout = self.currentListViewLayout { - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: currentListViewLayout.size, insets: currentListViewLayout.insets, scrollIndicatorInsets: currentListViewLayout.scrollIndicatorInsets, duration: 0.0, curve: .Default(duration: nil), ensureTopInsetForOverlayHighlightedItems: nil, customAnimationTransition: nil) - historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false, completion: {}) - } - - if let validLayout = self.validLayout, transition.isAnimated, let tabSwitchDirection { - var offsetMultiplier = CGPoint() - switch tabSwitchDirection { - case .up: - offsetMultiplier.y = -1.0 - case .down: - offsetMultiplier.y = 1.0 - case .left: - offsetMultiplier.x = -1.0 - case .right: - offsetMultiplier.x = 1.0 - } - - previousHistoryNode.clipsToBounds = true - historyNode.clipsToBounds = true - - transition.animatePosition(layer: historyNode.layer, from: CGPoint(x: offsetMultiplier.x * validLayout.0.size.width, y: offsetMultiplier.y * validLayout.0.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) - transition.animatePosition(layer: previousHistoryNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * validLayout.0.size.width, y: -offsetMultiplier.y * validLayout.0.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousHistoryNode, weak historyNode] _ in - previousHistoryNode?.removeFromSupernode() - historyNode?.clipsToBounds = false - }) + if historyNode === self.historyNode { + historyNode.updateChatLocation(chatLocation: chatLocation) } else { - previousHistoryNode.removeFromSupernode() + self.pendingSwitchToChatLocation = PendingSwitchToChatLocation( + historyNode: historyNode, + animationDirection: animationDirection + ) } } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 9add155f1d..38a085cdaf 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -131,8 +131,20 @@ func chatHistoryEntriesForView( if case let .replyThread(replyThreadMessage) = location, replyThreadMessage.isForumPost { for media in message.media { - if let action = media as? TelegramMediaAction, case .topicCreated = action.action { - continue loop + if let action = media as? TelegramMediaAction { + if case .topicCreated = action.action { + continue loop + } else if case .groupCreated = action.action { + var chatPeer: Peer? + for entry in view.additionalData { + if case let .peer(_, peer) = entry { + chatPeer = peer + } + } + if let channel = chatPeer as? TelegramChannel, (channel.isMonoForum || channel.linkedBotId != nil) { + continue loop + } + } } } } else if case .peer = location { @@ -144,7 +156,7 @@ func chatHistoryEntriesForView( chatPeer = peer } } - if let channel = chatPeer as? TelegramChannel, channel.isMonoForum { + if let channel = chatPeer as? TelegramChannel, (channel.isMonoForum || channel.linkedBotId != nil) { continue loop } } @@ -223,7 +235,7 @@ func chatHistoryEntriesForView( isCentered = link.isCentered } - let attributes = ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: isCentered, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }) + let attributes = ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: isCentered, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }, displayContinueThreadFooter: false) let groupStableId = currentState.messageGroupStableId(messageStableId: message.stableId, groupId: messageGroupingKey, isLocal: Namespaces.Message.allLocal.contains(message.id.namespace)) var found = false @@ -269,7 +281,7 @@ func chatHistoryEntriesForView( isCentered = link.isCentered } - entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: isCentered, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }))) + entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: isCentered, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }, displayContinueThreadFooter: false))) } } else { let selection: ChatHistoryMessageSelection @@ -279,7 +291,7 @@ func chatHistoryEntriesForView( selection = .none } - entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }))) + entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }, displayContinueThreadFooter: false))) } } @@ -299,6 +311,30 @@ func chatHistoryEntriesForView( entries = flatEntries } + var addBotForumHeader = false + if location.threadId == nil, let channel = chatPeer as? TelegramChannel, channel.linkedBotId != nil, !entries.isEmpty, !view.holeEarlier, !view.isLoading { + addBotForumHeader = true + outer: for i in (0 ..< entries.count).reversed() { + switch entries[i] { + case let .MessageEntry(message, presentationData, isRead, location, selection, attributes): + if message.threadId == nil { + continue outer + } + for media in message.media { + if let _ = media as? TelegramMediaAction { + continue outer + } + } + var attributes = attributes + attributes.displayContinueThreadFooter = true + entries[i] = .MessageEntry(message, presentationData, isRead, location, selection, attributes) + break outer + default: + break + } + } + } + let insertPendingProcessingMessage: ([Message], Int) -> Void = { messages, index in let serviceMessage = Message( stableId: UInt32.max - messages[0].stableId, @@ -326,7 +362,7 @@ func chatHistoryEntriesForView( associatedThreadInfo: nil, associatedStories: [:] ) - entries.insert(.MessageEntry(serviceMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: index) + entries.insert(.MessageEntry(serviceMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)), at: index) } for i in (0 ..< entries.count).reversed() { @@ -366,7 +402,7 @@ func chatHistoryEntriesForView( } } if let insertAtPosition { - entries.insert(.MessageEntry(joinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: insertAtPosition) + entries.insert(.MessageEntry(joinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)), at: insertAtPosition) } } } @@ -428,12 +464,12 @@ func chatHistoryEntriesForView( if messages.count > 1, let groupingKey = messages[0].groupingKey { var groupMessages: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)] = [] for message in messages { - groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }), nil)) + groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false, isCentered: false, authorStoryStats: message.author.flatMap { view.peerStoryStats[$0.id] }, displayContinueThreadFooter: false), nil)) } entries.insert(.MessageGroupEntry(groupingKey, groupMessages, presentationData), at: 0) } else { if !hasTopicCreated { - entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false, isCentered: false, authorStoryStats: messages[0].author.flatMap { view.peerStoryStats[$0.id] })), at: 0) + entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false, isCentered: false, authorStoryStats: messages[0].author.flatMap { view.peerStoryStats[$0.id] }, displayContinueThreadFooter: false)), at: 0) } } @@ -507,7 +543,7 @@ func chatHistoryEntriesForView( associatedThreadInfo: nil, associatedStories: [:] ) - entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: 0) + entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)), at: 0) } if let chatPeer, let nameChangeDate = peerStatusSettings.nameChangeDate, nameChangeDate > 0 { @@ -547,7 +583,7 @@ func chatHistoryEntriesForView( associatedThreadInfo: nil, associatedStories: [:] ) - entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: 0) + entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)), at: 0) } if let peer = chatPeer.flatMap(EnginePeer.init) { @@ -600,7 +636,7 @@ func chatHistoryEntriesForView( if !dynamicAdMessages.isEmpty { assert(entries.sorted() == entries) for message in dynamicAdMessages { - entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) + entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false))) } entries.sort() } @@ -635,7 +671,7 @@ func chatHistoryEntriesForView( associatedStories: message.associatedStories ) nextAdMessageId += 1 - entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) + entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false))) } } } else if includeSearchEntry { @@ -645,6 +681,9 @@ func chatHistoryEntriesForView( } } } + if addBotForumHeader { + entries.append(.ChatInfoEntry(.newThreadInfo, presentationData)) + } if includeEmbeddedSavedChatInfo, let peerId = location.peerId { if !view.isLoading && view.laterId == nil { let string = presentationData.strings.Chat_SavedMessagesTabInfoText @@ -697,7 +736,7 @@ func chatHistoryEntriesForView( associatedThreadInfo: nil, associatedStories: [:] ) - entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) + entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false))) } } @@ -754,7 +793,7 @@ func chatHistoryEntriesForView( associatedThreadInfo: nil, associatedStories: [:] ) - entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil)), at: 0) + entries.insert(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)), at: 0) } } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index a8e4637165..efc125f26c 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -37,6 +37,7 @@ import ChatControllerInteraction import DustEffect import UrlHandling import TextFormat +import ChatNewThreadInfoItem struct ChatTopVisibleMessageRange: Equatable { var lowerBound: MessageIndex @@ -215,7 +216,7 @@ extension ListMessageItemInteraction { } } -private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, entries: [ChatHistoryViewTransitionInsertEntry]) -> [ListViewInsertItem] { +private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionInsertEntry]) -> [ListViewInsertItem] { var disableFloatingDateHeaders = false if case .customChatContents = chatLocation { disableFloatingDateHeaders = true @@ -238,7 +239,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) + item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .MessageGroupEntry(_, messages, presentationData): @@ -262,6 +263,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca item = ChatBotInfoItem(title: title, text: text, photo: photo, video: video, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) case let .userInfo(peer, verification, registrationDate, phoneCountry, groupsInCommonCount): item = ChatUserInfoItem(peer: peer, verification: verification, registrationDate: registrationDate, phoneCountry: phoneCountry, groupsInCommonCount: groupsInCommonCount, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) + case .newThreadInfo: + item = ChatNewThreadInfoItem(controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .SearchEntry(theme, strings): @@ -272,7 +275,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca } } -private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, entries: [ChatHistoryViewTransitionUpdateEntry]) -> [ListViewUpdateItem] { +private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionUpdateEntry]) -> [ListViewUpdateItem] { var disableFloatingDateHeaders = false if case .customChatContents = chatLocation { disableFloatingDateHeaders = true @@ -295,7 +298,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) + item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .MessageGroupEntry(_, messages, presentationData): @@ -319,6 +322,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca item = ChatBotInfoItem(title: title, text: text, photo: photo, video: video, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) case let .userInfo(peer, verification, registrationDate, phoneCountry, groupsInCommonCount): item = ChatUserInfoItem(peer: peer, verification: verification, registrationDate: registrationDate, phoneCountry: phoneCountry, groupsInCommonCount: groupsInCommonCount, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) + case .newThreadInfo: + item = ChatNewThreadInfoItem(controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .SearchEntry(theme, strings): @@ -329,11 +334,11 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca } } -private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, animateFromPreviousFilter: Bool, transition: ChatHistoryViewTransition) -> ChatHistoryListViewTransition { - return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: animateFromPreviousFilter) +private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, animateFromPreviousFilter: Bool, transition: ChatHistoryViewTransition) -> ChatHistoryListViewTransition { + return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: animateFromPreviousFilter) } -private final class ChatHistoryTransactionOpaqueState { +final class ChatHistoryTransactionOpaqueState { let historyView: ChatHistoryView init(historyView: ChatHistoryView) { @@ -842,7 +847,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, - linkedMonoforumId: nil + linkedMonoforumId: nil, + linkedBotId: nil ) messagePeers[author.id] = author @@ -979,7 +985,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto self.preloadPages = false self.beginChatHistoryTransitions(resetScrolling: false, switchedToAnotherSource: false) - self.beginReadHistoryManagement() if let subject = subject, case let .message(messageSubject, highlight, _, setupReply) = subject { @@ -1226,6 +1231,16 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto self.beginChatHistoryTransitions(resetScrolling: true, switchedToAnotherSource: false) } + public func updateChatLocation(chatLocation: ChatLocation) { + if self.chatLocation == chatLocation { + return + } + self.chatLocation = chatLocation + + self.beginChatHistoryTransitions(resetScrolling: true, switchedToAnotherSource: false) + self.beginReadHistoryManagement() + } + private func beginAdMessageManagement(adMessages: Signal<(interPostInterval: Int32?, messages: [Message], startDelay: Int32?, betweenDelay: Int32?), NoError>) { self.adMessagesDisposable = (adMessages |> deliverOnMainQueue).startStrict(next: { [weak self] interPostInterval, messages, _, _ in @@ -1380,11 +1395,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let currentViewVersion = self.currentViewVersion - let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?, Set), NoError> + var historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?, Set), NoError> var isFirstTime = true - var updateAllOnEachVersion = false - if case let .custom(messages, at, quote, _) = self.source { - updateAllOnEachVersion = true + var isSavedMusic = false + var canReorder = false + if case let .custom(messages, at, quote, isSavedMusicValue, canReorderValue, _) = self.source { + isSavedMusic = isSavedMusicValue + canReorder = canReorderValue historyViewUpdate = messages |> map { messages, _, hasMore in let version = currentViewVersion.modify({ value in @@ -1745,6 +1762,33 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } |> distinctUntilChanged + let stopHistoryViewUpdates: Signal + if let peerId = chatLocation.peerId, (chatLocation.threadId == EngineMessage.newTopicThreadId || chatLocation.threadId == nil) { + stopHistoryViewUpdates = Signal.single(false) + |> then( + self.context.account.pendingMessageManager.newTopicEvents(peerId: peerId) + |> mapToSignal { event -> Signal in + if case .willMove = event { + return .single(true) + } else { + return .never() + } + } + |> take(1) + ) + } else { + stopHistoryViewUpdates = .single(false) + } + + let historyViewUpdateValue = historyViewUpdate + historyViewUpdate = stopHistoryViewUpdates |> mapToSignal { value in + if value { + return .never() + } else { + return historyViewUpdateValue + } + } + let startTime = CFAbsoluteTimeGetCurrent() var measure_isFirstTime = true let messageViewQueue = Queue.mainQueue() @@ -1873,8 +1917,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let disableAnimations = true let forceSynchronous = true - let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: nil, scrollAnimationCurve: nil, initialData: initialData?.initialData, keyboardButtonsMessage: nil, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: false, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: false) - var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: previousViewValue.associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: 0, animateFromPreviousFilter: resetScrolling, transition: rawTransition) + let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: nil, scrollAnimationCurve: nil, initialData: initialData?.initialData, keyboardButtonsMessage: nil, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: false, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: false) + var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: previousViewValue.associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: 0, isSavedMusic: isSavedMusic, canReorder: canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition) if disableAnimations { mappedTransition.options.remove(.AnimateInsertion) @@ -2009,7 +2053,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } var isSuspiciousPeer = false - if let cachedUserData = data.cachedData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.flags.contains(.canBlock) { + if let cachedUserData = data.cachedData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.flags.contains(.canBlock) || peerStatusSettings.flags.contains(.canReport) { isSuspiciousPeer = true } @@ -2264,8 +2308,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto keyboardButtonsMessage = nil } - let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: updateAllOnEachVersion || forceUpdateAll) - var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, animateFromPreviousFilter: resetScrolling, transition: rawTransition) + let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: !isSavedMusic || forceUpdateAll) + var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: processedView.filteredEntries.count > 1 && canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition) if disableAnimations { mappedTransition.options.remove(.AnimateInsertion) @@ -2303,6 +2347,10 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto self.historyDisposable.set(historyViewTransitionDisposable.strict()) } + func stopHistoryUpdates() { + self.historyDisposable.set(nil) + } + private func beginReadHistoryManagement() { let previousMaxIncomingMessageIndexByNamespace = Atomic<[MessageId.Namespace: MessageIndex]>(value: [:]) let readHistory = combineLatest(self.maxVisibleIncomingMessageIndex.get(), self.canReadHistory.get()) @@ -2338,6 +2386,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } }).strict()) + self.canReadHistoryDisposable?.dispose() self.canReadHistoryDisposable = (self.canReadHistory.get() |> deliverOnMainQueue).startStrict(next: { [weak self, weak context] value in if let strongSelf = self { if strongSelf.canReadHistoryValue != value { @@ -4450,6 +4499,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto currentMessage = messages.first?.0 } break loop + } else if case .ChatInfoEntry = entry { + break loop } } index += 1 diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index f7c4df78dd..c762d6e293 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1131,7 +1131,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState let sendGiftTitle: String var isIncoming = message.effectivelyIncoming(context.account.peerId) for media in message.media { - if let action = media as? TelegramMediaAction, case let .starGiftUnique(_, isUpgrade, _, _, _, _, _, _, _, _, _, _, _) = action.action { + if let action = media as? TelegramMediaAction, case let .starGiftUnique(_, isUpgrade, _, _, _, _, _, _, _, _, _, _, _, _) = action.action { if isUpgrade && message.author?.id == context.account.peerId { isIncoming = true } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 3aba8f546d..9463cee75c 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -122,6 +122,22 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present } } + if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, channel.linkedBotId != nil, case .peer = presentationInterfaceState.chatLocation { + let displaySearch = hasMessages + + if displaySearch { + if case .search(false) = currentButton?.action { + return currentButton + } else { + let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(presentationInterfaceState.theme), style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = strings.Conversation_Search + return ChatNavigationButton(action: .search(hasTags: false), buttonItem: buttonItem) + } + } else { + return nil + } + } + if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let moreInfoNavigationButton = moreInfoNavigationButton { if case .replyThread = presentationInterfaceState.chatLocation { } else { @@ -132,6 +148,13 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present } } + if let user = presentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), let moreInfoNavigationButton = moreInfoNavigationButton { + if case .pinnedMessages = presentationInterfaceState.subject { + } else { + return moreInfoNavigationButton + } + } + if case .messageOptions = presentationInterfaceState.subject { return nil } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index af06d64b1e..c0baff10b0 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -237,7 +237,7 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { return currentPanel } else { - let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: true) + let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: .monoforum) panel.interfaceInteraction = interfaceInteraction return panel } @@ -248,7 +248,18 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { return currentPanel } else { - let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: false) + let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: channel.linkedBotId != nil ? .botForum : .forum) + panel.interfaceInteraction = interfaceInteraction + return panel + } + } + } else if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), chatPresentationInterfaceState.search == nil { + let topicListDisplayModeOnTheSide = chatPresentationInterfaceState.persistentData.topicListPanelLocation + if !topicListDisplayModeOnTheSide, let peerId = chatPresentationInterfaceState.chatLocation.peerId { + if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { + return currentPanel + } else { + let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: .botForum) panel.interfaceInteraction = interfaceInteraction return panel } @@ -279,7 +290,7 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState strings: chatPresentationInterfaceState.strings, location: .side, peerId: peerId, - isMonoforum: true, + kind: .monoforum, topicId: chatPresentationInterfaceState.chatLocation.threadId, controller: { [weak interfaceInteraction] in return interfaceInteraction?.chatController() @@ -310,7 +321,38 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState strings: chatPresentationInterfaceState.strings, location: .side, peerId: peerId, - isMonoforum: false, + kind: channel.linkedBotId != nil ? .botForum : .forum, + topicId: chatPresentationInterfaceState.chatLocation.threadId, + controller: { [weak interfaceInteraction] in + return interfaceInteraction?.chatController() + }, + togglePanel: { [weak interfaceInteraction] in + interfaceInteraction?.toggleChatSidebarMode() + }, + updateTopicId: { [weak interfaceInteraction] topicId, direction in + interfaceInteraction?.updateChatLocationThread(topicId, direction ? .down : .up) + }, + openDeletePeer: { [weak interfaceInteraction] threadId in + guard let controller = interfaceInteraction?.chatController() as? ChatControllerImpl else { + return + } + controller.openDeleteMonoforumPeer(peerId: EnginePeer.Id(threadId)) + } + )) + ) + } + } else if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), chatPresentationInterfaceState.search == nil { + let topicListDisplayModeOnTheSide = chatPresentationInterfaceState.persistentData.topicListPanelLocation + if topicListDisplayModeOnTheSide { + return AnyComponentWithIdentity( + id: "topics", + component: AnyComponent(ChatSideTopicsPanel( + context: context, + theme: chatPresentationInterfaceState.theme, + strings: chatPresentationInterfaceState.strings, + location: .side, + peerId: peerId, + kind: .botForum, topicId: chatPresentationInterfaceState.chatLocation.threadId, controller: { [weak interfaceInteraction] in return interfaceInteraction?.chatController() diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 9fec1c3b27..f08548e652 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -381,6 +381,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { self.textNode = ImmediateTextNode() self.textNode.maximumNumberOfLines = 3 + self.textNode.truncationType = .middle self.textNode.textAlignment = .center super.init() @@ -556,7 +557,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { transition.updateAlpha(node: self.textNode, alpha: 1.0) - let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 80.0, height: 40.0)) + let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 80.0, height: 80.0)) self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - textSize.width) / 2.0), y: 10.0), size: textSize) for (_, view) in self.buttons { @@ -565,7 +566,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { self.tapGestureRecognizer?.isEnabled = true - panelHeight += 15.0 + panelHeight += max(15.0, textSize.height - 19.0) } else { transition.updateAlpha(node: self.textNode, alpha: 0.0) diff --git a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift index dee8e42844..de106edb35 100644 --- a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift @@ -191,7 +191,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { if case .everything = search.domain { if let _ = params.interfaceState.renderedPeer?.peer as? TelegramGroup { canSearchMembers = true - } else if let peer = params.interfaceState.renderedPeer?.peer as? TelegramChannel, case .group = peer.info, !peer.isMonoForum { + } else if let peer = params.interfaceState.renderedPeer?.peer as? TelegramChannel, case .group = peer.info, !peer.isMonoForum, peer.linkedBotId == nil { canSearchMembers = true } } else { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 3a6d8dc44e..fa0851362f 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1919,6 +1919,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } else if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum, let forumTopicData = interfaceState.forumTopicData { if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { placeholder = interfaceState.strings.Chat_InputPlaceholderReplyInTopic(threadInfo.title).string + } else if let _ = channel.linkedBotId, interfaceState.chatLocation.threadId == nil { + placeholder = interfaceState.strings.Conversation_InputTextPlaceholder } else { placeholder = interfaceState.strings.Chat_InputPlaceholderMessageInTopic(forumTopicData.title).string } diff --git a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift index b913f1078c..0da1b30f5b 100644 --- a/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTopicListTitleAccessoryPanelNode.swift @@ -53,17 +53,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C private let context: AccountContext private let peerId: EnginePeer.Id - private let isMonoforum: Bool + private let kind: ChatSideTopicsPanel.Kind private let panel = ComponentView() - init(context: AccountContext, peerId: EnginePeer.Id, isMonoforum: Bool) { + init(context: AccountContext, peerId: EnginePeer.Id, kind: ChatSideTopicsPanel.Kind) { self.context = context self.peerId = peerId - self.isMonoforum = isMonoforum + self.kind = kind super.init() - - } deinit { @@ -103,7 +101,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C strings: params.interfaceState.strings, location: .top, peerId: self.peerId, - isMonoforum: self.isMonoforum, + kind: self.kind, topicId: params.interfaceState.chatLocation.threadId, controller: { [weak self] in return self?.interfaceInteraction?.chatController() diff --git a/submodules/TelegramUI/Sources/MediaManager.swift b/submodules/TelegramUI/Sources/MediaManager.swift index 1d7556ecec..a15b8172b7 100644 --- a/submodules/TelegramUI/Sources/MediaManager.swift +++ b/submodules/TelegramUI/Sources/MediaManager.swift @@ -15,6 +15,7 @@ import TelegramUniversalVideoContent import DeviceProximity import MediaResources import PhotoResources +import PeerMessagesMediaPlaylist enum SharedMediaPlayerGroup: Int { case music = 0 diff --git a/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift b/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift index 5870ae8426..4d055b9b80 100644 --- a/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift +++ b/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift @@ -51,6 +51,9 @@ final class NotificationContainerControllerNode: ASDisplayNode { } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha == 0.0 { + return nil + } if let (_, blockingItemNode) = self.blockingItemAndNode { return blockingItemNode.hitTest(point, with: event) } diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 6abd752090..8d1943adcc 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -25,6 +25,7 @@ import GalleryData import StoryContainerScreen import WallpaperGalleryScreen import BrowserUI +import PeerMessagesMediaPlaylist func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { var story: TelegramMediaStory? diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index cea864b2ff..ae30ec10c5 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -343,7 +343,7 @@ func openResolvedUrlImpl( if let photoRepresentation = invite.photoRepresentation { photo.append(photoRepresentation) } - let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil) + let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil, verificationIconFileId: nil, sendPaidMessageStars: nil, linkedMonoforumId: nil, linkedBotId: nil) let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount.value, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0) inputData.set(.single(BotCheckoutController.InputData( @@ -1481,7 +1481,7 @@ func openResolvedUrlImpl( navigationController?.pushViewController(controller) } } - }, dismissed: { + }, openChatTheme: nil, dismissed: { dismissedImpl?() }) navigationController?.pushViewController(controller) @@ -1549,5 +1549,19 @@ func openResolvedUrlImpl( } navigationController?.pushViewController(controller) } + case let .sendGift(peerId): + Task { @MainActor [weak navigationController] in + if let peerId { + let giftOptions = await (context.engine.payments.premiumGiftCodeOptions(peerId: nil, onlyCached: true) |> filter { !$0.isEmpty }).get() + + let premiumOptions = giftOptions.filter { $0.users == 1 }.map { CachedPremiumGiftOption(months: $0.months, currency: $0.currency, amount: $0.amount, botUrl: "", storeProductId: $0.storeProductId) } + let controller = context.sharedContext.makeGiftOptionsController(context: context, peerId: peerId, premiumOptions: premiumOptions, hasBirthday: false, completion: nil) + controller.navigationPresentation = .modal + navigationController?.pushViewController(controller) + } else { + let controller = context.sharedContext.makePremiumGiftController(context: context, source: .settings(nil), completion: nil) + navigationController?.pushViewController(controller) + } + } } } diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 0290a0d907..12c79166cd 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -1037,6 +1037,35 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur } } } + } else if parsedUrl.host == "send_gift" { + var recipient: String? + if let components = URLComponents(string: "/?" + query) { + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "to" { + recipient = value + break + } + } + } + } + } + if let recipient { + if let id = Int64(recipient) { + handleResolvedUrl(.sendGift(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)))) + } else { + let _ = (context.engine.peers.resolvePeerByName(name: recipient, referrer: nil) + |> deliverOnMainQueue).start(next: { result in + guard case let .result(peer) = result, let peer else { + return + } + handleResolvedUrl(.sendGift(peerId: peer.id)) + }) + } + } else { + handleResolvedUrl(.sendGift(peerId: nil)) + } } } else { if parsedUrl.host == "stars" { @@ -1087,6 +1116,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur context.sharedContext.presentGlobalController(alertController, nil) } }) + } else if parsedUrl.host == "send_gift" { + handleResolvedUrl(.sendGift(peerId: nil)) } } diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift index e96f9c4833..d365e1f937 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerController.swift @@ -17,7 +17,7 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer let initialOrder: MusicPlaybackSettingsOrder let playlistLocation: SharedMediaPlaylistLocation? - private weak var parentNavigationController: NavigationController? + private(set) weak var parentNavigationController: NavigationController? private var animatedIn = false @@ -27,7 +27,15 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer private var accountInUseDisposable: Disposable? - init(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation? = nil, parentNavigationController: NavigationController?) { + init( + context: AccountContext, + chatLocation: ChatLocation, + type: MediaManagerPlayerType, + initialMessageId: MessageId, + initialOrder: MusicPlaybackSettingsOrder, + playlistLocation: SharedMediaPlaylistLocation? = nil, + parentNavigationController: NavigationController? + ) { self.context = context self.chatLocation = chatLocation self.type = type @@ -56,87 +64,86 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer override public func loadDisplayNode() { self.displayNode = OverlayAudioPlayerControllerNode(context: self.context, chatLocation: self.chatLocation, type: self.type, initialMessageId: self.initialMessageId, initialOrder: self.initialOrder, playlistLocation: self.playlistLocation, requestDismiss: { [weak self] in self?.dismiss() - }, requestShare: { [weak self] messageId in + }, requestShare: { [weak self] subject in if let strongSelf = self { - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) - |> deliverOnMainQueue).startStandalone(next: { message in - if let strongSelf = self, let message = message { - let shareController = ShareController(context: strongSelf.context, subject: .messages([message._asMessage()]), showInChat: { message in - if let strongSelf = self { - strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: message.id.peerId, messageId: message.id) - strongSelf.dismiss() - } - }, externalShare: true) - shareController.completed = { [weak self] peerIds in - if let strongSelf = self { - let _ = (strongSelf.context.engine.data.get( - EngineDataList( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) - ) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in - if let strongSelf = self { - let peers = peerList.compactMap { $0 } - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - - let text: String - var savedMessages = false - if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { - text = presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - peerName = peerName.replacingOccurrences(of: "**", with: "") - text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "") - var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "") - text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - peerName = peerName.replacingOccurrences(of: "**", with: "") - text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string - } else { - text = "" - } - } - - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if savedMessages, let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) - }) - } - return false - }), in: .current) - } - }) - } - } - strongSelf.controllerNode.view.endEditing(true) - strongSelf.present(shareController, in: .window(.root)) + var canShowInChat = false + if case .messages = subject { + canShowInChat = true + } + let shareController = ShareController(context: strongSelf.context, subject: subject, showInChat: canShowInChat ? { message in + if let strongSelf = self { + strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: message.id.peerId, messageId: message.id) + strongSelf.dismiss() } - }) + } : nil, externalShare: true) + shareController.completed = { [weak self] peerIds in + if let strongSelf = self { + let _ = (strongSelf.context.engine.data.get( + EngineDataList( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) + ) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in + if let strongSelf = self { + let peers = peerList.compactMap { $0 } + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + + let text: String + var savedMessages = false + if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { + text = presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One + savedMessages = true + } else { + if peers.count == 1, let peer = peers.first { + var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + peerName = peerName.replacingOccurrences(of: "**", with: "") + text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string + } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { + var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "") + var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "") + text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string + } else if let peer = peers.first { + var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + peerName = peerName.replacingOccurrences(of: "**", with: "") + text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.parentNavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .current) + } + }) + } + } + strongSelf.controllerNode.view.endEditing(true) + strongSelf.present(shareController, in: .window(.root)) } }, requestSearchByArtist: { [weak self] artist in if let strongSelf = self { strongSelf.context.sharedContext.openSearch(filter: .music, query: artist) strongSelf.dismiss() } - }) - self.controllerNode.getParentController = { [weak self] in + }, + getParentController: { [weak self] in return self - } + }) self.ready.set(self.controllerNode.ready.get()) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 2456775353..8311472202 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -11,17 +11,21 @@ import AccountContext import DirectionalPanGesture import ChatPresentationInterfaceState import ChatControllerInteraction +import ContextUI +import UndoUI +import ChatHistoryEntry final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestureRecognizerDelegate { let ready = Promise() private let context: AccountContext + private let source: ChatHistoryListSource private let chatLocation: ChatLocation private var presentationData: PresentationData private let type: MediaManagerPlayerType private let requestDismiss: () -> Void - private let requestShare: (MessageId) -> Void + private let requestShare: (ShareControllerSubject) -> Void private let requestSearchByArtist: (String) -> Void private let playlistLocation: SharedMediaPlaylistLocation? private let isGlobalSearch: Bool @@ -40,18 +44,33 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu private var replacementHistoryNode: ChatHistoryListNodeImpl? private var replacementHistoryNodeFloatingOffset: CGFloat? + private var saveMediaDisposable: MetaDisposable? + private var validLayout: ContainerViewLayout? private var presentationDataDisposable: Disposable? private let replacementHistoryNodeReadyDisposable = MetaDisposable() - var getParentController: () -> ViewController? = { return nil } { - didSet { - self.controlsNode.getParentController = self.getParentController - } - } + private let getParentController: () -> ViewController? + + private var savedIdsDisposable: Disposable? + private var savedIdsPromise = Promise?>() + private var savedIds: Set? - init(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, requestDismiss: @escaping () -> Void, requestShare: @escaping (MessageId) -> Void, requestSearchByArtist: @escaping (String) -> Void) { + private var copyProtectionEnabled = false + + init( + context: AccountContext, + chatLocation: ChatLocation, + type: MediaManagerPlayerType, + initialMessageId: MessageId, + initialOrder: MusicPlaybackSettingsOrder, + playlistLocation: SharedMediaPlaylistLocation?, + requestDismiss: @escaping () -> Void, + requestShare: @escaping (ShareControllerSubject) -> Void, + requestSearchByArtist: @escaping (String) -> Void, + getParentController: @escaping () -> ViewController? + ) { self.context = context self.chatLocation = chatLocation self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -60,6 +79,15 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.requestShare = requestShare self.requestSearchByArtist = requestSearchByArtist self.playlistLocation = playlistLocation + self.getParentController = getParentController + + if let playlistLocation = playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, canReorder, at, loadMore) = playlistLocation.effectiveLocation(context: context) { + self.source = .custom(messages: messages, messageId: at, quote: nil, isSavedMusic: true, canReorder: canReorder, loadMore: loadMore) + self.isGlobalSearch = false + } else { + self.source = .default + self.isGlobalSearch = false + } if case .regular = initialOrder { self.currentIsReversed = false @@ -68,6 +96,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } var openMessageImpl: ((MessageId) -> Bool)? + var openMessageContextMenuImpl: ((Message, ASDisplayNode, CGRect, Any?) -> Void)? self.controllerInteraction = ChatControllerInteraction(openMessage: { message, _ in if let openMessageImpl = openMessageImpl { return openMessageImpl(message.id) @@ -76,7 +105,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } }, openPeer: { _, _, _, _ in }, openPeerMention: { _, _ in - }, openMessageContextMenu: { _, _, _, _, _, _ in + }, openMessageContextMenu: { message, _, node, rect, gesture, _ in + openMessageContextMenuImpl?(message, node, rect, gesture) }, openMessageReactionContextMenu: { _, _, _, _ in }, updateMessageReaction: { _, _, _, _ in }, activateMessagePinch: { _ in @@ -205,7 +235,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.contentNode = ASDisplayNode() - self.controlsNode = OverlayPlayerControlsNode(account: context.account, engine: context.engine, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState) + self.controlsNode = OverlayPlayerControlsNode(account: context.account, engine: context.engine, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState, chatLocation: self.chatLocation, source: self.source) + self.controlsNode.getParentController = getParentController self.historyBackgroundNode = ASDisplayNode() self.historyBackgroundNode.isLayerBacked = true @@ -228,16 +259,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let chatLocationContextHolder = Atomic(value: nil) - let source: ChatHistoryListSource - if let playlistLocation = playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, at, loadMore) = playlistLocation { - source = .custom(messages: messages, messageId: at, quote: nil, loadMore: loadMore) - self.isGlobalSearch = true - } else { - source = .default - self.isGlobalSearch = false - } - - self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) + self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) self.historyNode.clipsToBounds = true super.init() @@ -247,6 +269,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.historyNode.preloadPages = true self.historyNode.stackFromBottom = true + self.historyNode.areContentAnimationsEnabled = true self.historyNode.updateFloatingHeaderOffset = { [weak self] offset, transition in if let strongSelf = self { strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition) @@ -254,13 +277,17 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } self.historyNode.endedInteractiveDragging = { [weak self] _ in - guard let strongSelf = self else { + guard let self else { return } - switch strongSelf.historyNode.visibleContentOffset() { + switch self.historyNode.visibleContentOffset() { case let .known(value): - if value <= -10.0 { - strongSelf.requestDismiss() + if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder { + + } else { + if value <= -10.0 { + self.requestDismiss() + } } default: break @@ -269,7 +296,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.controlsNode.updateIsExpanded = { [weak self] in if let strongSelf = self, let validLayout = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(validLayout, transition: .animated(duration: 0.3, curve: .spring)) + strongSelf.containerLayoutUpdated(validLayout, transition: .animated(duration: 0.5, curve: .spring)) } } @@ -277,14 +304,20 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self?.requestDismiss() } - self.controlsNode.requestShare = { [weak self] messageId in - self?.requestShare(messageId) + self.controlsNode.requestShare = { [weak self] subject in + self?.requestShare(subject) } self.controlsNode.requestSearchByArtist = { [weak self] artist in self?.requestSearchByArtist(artist) } + self.controlsNode.requestLayout = { [weak self] transition in + if let self, let validLayout = self.validLayout { + self.containerLayoutUpdated(validLayout, transition: transition) + } + } + self.controlsNode.updateOrder = { [weak self] order in if let strongSelf = self { let reversed: Bool @@ -308,6 +341,18 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } } + self.controlsNode.requestSaveToProfile = { [weak self] file in + if let self { + self.addToSavedMusic(file: file) + } + } + + self.controlsNode.requestRemoveFromProfile = { [weak self] file in + if let self { + self.removeFromSavedMusic(file: file) + } + } + self.addSubnode(self.dimNode) self.addSubnode(self.contentNode) self.contentNode.addSubnode(self.historyBackgroundNode) @@ -321,8 +366,12 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu openMessageImpl = { [weak self] id in if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.historyNode.messageInCurrentHistoryView(id) { var playlistLocation: PeerMessagesPlaylistLocation? - if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, _, loadMore) = location { - playlistLocation = .custom(messages: messages, at: id, loadMore: loadMore) + if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation { + if case let .custom(messages, canReorder, _, loadMore) = location { + playlistLocation = .custom(messages: messages, canReorder: canReorder, at: id, loadMore: loadMore) + } else if case let .savedMusic(context, _, canReorder) = location { + playlistLocation = .savedMusic(context: context, at: id.id, canReorder: canReorder) + } } return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _, _ in }, transitionNode: { _, _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in }, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation)) @@ -330,6 +379,13 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu return false } + openMessageContextMenuImpl = { [weak self] message, node, rect, gesture in + guard let self else { + return + } + self.openMessageContextMenu(message: message, node: node, frame: rect, gesture: gesture as? ContextGesture) + } + self.presentationDataDisposable = context.sharedContext.presentationData.startStrict(next: { [weak self] presentationData in if let strongSelf = self { if strongSelf.presentationData.theme !== presentationData.theme || strongSelf.presentationData.strings !== presentationData.strings { @@ -338,14 +394,56 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } }) - self.ready.set(self.historyNode.historyState.get() |> map { _ -> Bool in - return true - } |> take(1)) + let copyProtectionEnabled: Signal + if case let .peer(peerId) = self.chatLocation { + copyProtectionEnabled = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled(id: peerId)) + } else { + copyProtectionEnabled = .single(false) + } + + self.savedIdsDisposable = combineLatest( + queue: Queue.mainQueue(), + context.engine.peers.savedMusicIds(), + copyProtectionEnabled + ).start(next: { [weak self] savedIds, copyProtectionEnabled in + guard let self else { + return + } + let isFirstTime = self.savedIds == nil + self.savedIds = savedIds + self.savedIdsPromise.set(.single(savedIds)) + self.copyProtectionEnabled = copyProtectionEnabled + + let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : .animated(duration: 0.5, curve: .spring) + self.updateFloatingHeaderOffset(offset: self.floatingHeaderOffset ?? 0.0, transition: transition) + if let validLayout = self.validLayout { + self.containerLayoutUpdated(validLayout, transition: transition) + } + }) + + self.ready.set( + combineLatest( + self.historyNode.historyState.get() + |> take(1), + self.savedIdsPromise.get() + |> filter { + $0 != nil + } + |> take(1) + ) + |> map { _, _ -> Bool in + return true + } + ) + + self.setupReordering() } deinit { self.presentationDataDisposable?.dispose() self.replacementHistoryNodeReadyDisposable.dispose() + self.savedIdsDisposable?.dispose() + self.saveMediaDisposable?.dispose() } override func didLoad() { @@ -358,11 +456,11 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu panRecognizer.delaysTouchesBegan = false panRecognizer.cancelsTouchesInView = true panRecognizer.shouldBegin = { [weak self] point in - guard let strongSelf = self else { + guard let self else { return false } - if strongSelf.controlsNode.bounds.contains(strongSelf.view.convert(point, to: strongSelf.controlsNode.view)) { - if strongSelf.controlsNode.frame.maxY <= strongSelf.historyNode.frame.minY { + if self.controlsNode.bounds.contains(self.view.convert(point, to: self.controlsNode.view)) { + if self.controlsNode.frame.maxY <= self.historyNode.frame.minY { return true } } @@ -371,6 +469,58 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.view.addGestureRecognizer(panRecognizer) } + private func setupReordering() { + guard let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, canReorder) = playlistLocation, canReorder else { + return + } + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: savedMusicContext.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer = peer.flatMap({ PeerReference($0._asPeer()) }) else { + return + } + + self.historyNode.reorderItem = { fromIndex, toIndex, transactionOpaqueState -> Signal in + guard let filteredEntries = (transactionOpaqueState as? ChatHistoryTransactionOpaqueState)?.historyView.filteredEntries, !filteredEntries.isEmpty else { + return .single(false) + } + + func mapIndex(_ uiIndex: Int) -> Int { + return filteredEntries.count - 1 - uiIndex + } + + let mappedFromIndex = mapIndex(fromIndex) + guard filteredEntries.indices.contains(mappedFromIndex), case let .MessageEntry(fromMessage, _, _, _, _, _) = filteredEntries[mappedFromIndex], let fromFile = fromMessage.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile else { + return .single(false) + } + + var afterFile: TelegramMediaFile? + if toIndex > 0 { + let afterMappedIndex = mapIndex(toIndex - 1) + if filteredEntries.indices.contains(afterMappedIndex), case let .MessageEntry(afterMessage, _, _, _, _, _) = filteredEntries[afterMappedIndex], let file = afterMessage.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile { + afterFile = file + } + } else { + afterFile = nil + } + + let _ = savedMusicContext.addMusic( + file: .savedMusic(peer: peer, media: fromFile), + afterFile: afterFile.flatMap { .savedMusic(peer: peer, media: $0) } + ).start() + + return .single(true) + } + }) + self.historyNode.autoScrollWhenReordering = false + self.historyNode.didEndScrollingWithOverscroll = { [weak self] in + guard let self else { + return + } + self.requestDismiss() + } + } + + func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData @@ -378,6 +528,148 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.controlsNode.updatePresentationData(self.presentationData) } + private func dismissAllTooltips() { + guard let controller = self.getParentController() else { + return + } + controller.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + controller.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + return true + }) + } + + func forwardToSavedMessages(file: FileMediaReference) { + self.dismissAllTooltips() + + let _ = self.context.engine.messages.enqueueOutgoingMessage(to: self.context.account.peerId, replyTo: nil, content: .file(file)).start() + + let controller = UndoOverlayController( + presentationData: self.presentationData, + content: .forward(savedMessages: true, text: "Audio forwarded to Saved Messages."), + action: { _ in + return true + } + ) + self.getParentController()?.present(controller, in: .window(.root)) + } + + private func updateMusicSaved(file: FileMediaReference, isSaved: Bool) { + if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, _) = playlistLocation, savedMusicContext.peerId == self.context.account.peerId { + if isSaved { + let _ = savedMusicContext.addMusic(file: file).start() + } else { + let _ = savedMusicContext.removeMusic(file: file).start() + } + } else { + if isSaved { + let _ = self.context.engine.peers.addSavedMusic(file: file).start() + } else { + let _ = self.context.engine.peers.removeSavedMusic(file: file).start() + } + } + } + + func addToSavedMusic(file: FileMediaReference) { + self.dismissAllTooltips() + + var actionText: String? = self.presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile_View + if let itemId = self.controlsNode.currentItemId as? PeerMessagesMediaPlaylistItemId, itemId.messageId.namespace == Namespaces.Message.Local && itemId.messageId.peerId == self.context.account.peerId { + actionText = nil + } + + let controller = UndoOverlayController( + presentationData: self.presentationData, + content: .universalImage( + image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!, + size: nil, + title: nil, + text: self.presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile, + customUndoText: actionText, + timeout: 3.0 + ), + action: { [weak self] action in + if let self, case .undo = action { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + if let controller = self.context.sharedContext.makePeerInfoController( + context: self.context, + updatedPresentationData: nil, + peer: peer._asPeer(), + mode: .myProfile, + avatarInitiallyExpanded: false, + fromChat: false, + requestsContext: nil + ) { + if let navigationController = (self.getParentController() as? OverlayAudioPlayerControllerImpl)?.parentNavigationController { + self.requestDismiss() + navigationController.pushViewController(controller) + } + } + }) + } + return true + } + ) + self.getParentController()?.present(controller, in: .window(.root)) + + self.updateMusicSaved(file: file, isSaved: true) + } + + func removeFromSavedMusic(file: FileMediaReference) { + self.dismissAllTooltips() + + let controller = UndoOverlayController( + presentationData: self.presentationData, + content: .universalImage( + image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!, + size: nil, + title: nil, + text: self.presentationData.strings.MediaPlayer_SavedMusic_RemovedFromProfile, + customUndoText: nil, + timeout: 3.0 + ), + action: { _ in + return true + } + ) + + if let itemId = self.controlsNode.currentItemId as? PeerMessagesMediaPlaylistItemId, itemId.messageId.namespace == Namespaces.Message.Local && itemId.messageId.peerId == self.context.account.peerId, self.historyNode.originalHistoryView?.entries.count == 1 { + if let navigationController = (self.getParentController() as? OverlayAudioPlayerControllerImpl)?.parentNavigationController { + self.requestDismiss() + navigationController.presentOverlay(controller: controller) + + self.context.sharedContext.mediaManager.setPlaylist(nil, type: self.type, control: .playback(.pause)) + } + } else { + self.getParentController()?.present(controller, in: .window(.root)) + } + + self.updateMusicSaved(file: file, isSaved: false) + } + + private var isSaved: Bool? { + if self .copyProtectionEnabled { + return nil + } + if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + return nil + } + guard let fileReference = self.controlsNode.currentFileReference else { + return nil + } + return self.savedIds?.contains(fileReference.media.fileId.id) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.validLayout = layout @@ -397,7 +689,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5) - let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded) + let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved) let listTopInset = layoutTopInset + controlsHeight @@ -405,10 +697,17 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5)) + var itemOffsetInsets = insets + if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder { + itemOffsetInsets.top = 0.0 + itemOffsetInsets.bottom = 0.0 + insets = itemOffsetInsets + } + transition.updateFrame(node: self.historyNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize)) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: duration, curve: curve) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, itemOffsetInsets: itemOffsetInsets, duration: duration, curve: curve) self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets) if let replacementHistoryNode = self.replacementHistoryNode { let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil)) @@ -423,6 +722,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } func animateOut(completion: (() -> Void)?) { + self.dismissAllTooltips() + self.layer.animateBoundsOriginYAdditive(from: self.bounds.origin.y, to: -self.bounds.size.height, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in completion?() }) @@ -456,7 +757,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu self.requestDismiss() } } - + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let recognizer = gestureRecognizer as? UIPanGestureRecognizer { let location = recognizer.location(in: self.view) @@ -479,7 +780,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: - break + self.dismissAllTooltips() case .changed: let translation = recognizer.translation(in: self.contentNode.view) var bounds = self.contentNode.bounds @@ -533,7 +834,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let maxHeight = validLayout.size.height - layoutTopInset - floor(56.0 * 0.5) - let controlsHeight = self.controlsNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, transition: transition) + let controlsHeight = self.controlsNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, hasSectionHeader: true, savedMusic: self.isSaved, transition: transition) let listTopInset = layoutTopInset + controlsHeight @@ -580,10 +881,11 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } let chatLocationContextHolder = Atomic(value: nil) - let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: .default, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) + let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) historyNode.clipsToBounds = true historyNode.preloadPages = true historyNode.stackFromBottom = true + historyNode.areContentAnimationsEnabled = true historyNode.updateFloatingHeaderOffset = { [weak self] offset, _ in self?.replacementHistoryNodeFloatingOffset = offset } @@ -598,7 +900,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5) - let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded) + let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved) let listTopInset = layoutTopInset + controlsHeight @@ -626,6 +928,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu previousHistoryNode.disconnect() self.contentNode.insertSubnode(replacementHistoryNode, belowSubnode: self.historyNode) self.historyNode = replacementHistoryNode + self.setupReordering() if let validLayout = self.validLayout, let offset = self.replacementHistoryNodeFloatingOffset, let previousOffset = self.floatingHeaderOffset { let offsetDelta = offset - previousOffset @@ -634,7 +937,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let maxHeight = validLayout.size.height - layoutTopInset - floor(56.0 * 0.5) - let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded) + let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved) let listTopInset = layoutTopInset + controlsHeight @@ -695,7 +998,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let maxHeight = layout.size.height - layoutTopInset - floor(56.0 * 0.5) - let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded) + let controlsHeight = OverlayPlayerControlsNode.heightForLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: maxHeight, isExpanded: self.controlsNode.isExpanded, hasSectionHeader: true, savedMusic: self.isSaved) let listTopInset = layoutTopInset + controlsHeight @@ -703,13 +1006,287 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5)) + var itemOffsetInsets = insets + if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder { + itemOffsetInsets.top = 0.0 + itemOffsetInsets.bottom = 0.0 + insets = itemOffsetInsets + } + self.historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil)) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, itemOffsetInsets: itemOffsetInsets, duration: 0.0, curve: .Default(duration: nil)) self.historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets) self.historyNode.recursivelyEnsureDisplaySynchronously(true) } } } + + private func openMessageContextMenu(message: Message, node: ASDisplayNode, frame: CGRect, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, location: CGPoint? = nil) { + guard let node = node as? ContextExtractedContentContainingNode, let peer = message.peers[message.id.peerId].flatMap({ PeerReference($0) }), let file = message.media.first(where: { $0 is TelegramMediaFile}) as? TelegramMediaFile else { + return + } + let context = self.context + let presentationData = self.presentationData + let source: ContextContentSource = .extracted(OverlayAudioPlayerContextExtractedContentSource(contentNode: node)) + let fileReference: FileMediaReference = message.id.namespace == Namespaces.Message.Local ? .savedMusic(peer: peer, media: file) : .message(message: MessageReference(message), media: file) + + let canSaveToProfile = !(self.savedIds?.contains(file.fileId.id) == true) + let canSaveToSavedMessages = message.id.peerId != self.context.account.peerId || message.id.namespace == Namespaces.Message.Local + + let _ = (context.sharedContext.chatAvailableMessageActions(engine: context.engine, accountPeerId: context.account.peerId, messageIds: [message.id], keepUpdated: false) + |> deliverOnMainQueue).startStandalone(next: { [weak self] actions in + guard let self else { + return + } + + var items: [ContextMenuItem] = [] + if canSaveToProfile || canSaveToSavedMessages { + items.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + if let self { + var subActions: [ContextMenuItem] = [] + subActions.append( + .action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in + c?.popItems() + })) + ) + subActions.append(.separator) + + if canSaveToProfile { + subActions.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Profile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + + if let self { + self.addToSavedMusic(file: fileReference) + } + })) + ) + } + + if canSaveToSavedMessages { + subActions.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_SavedMessages, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + + if let self { + self.forwardToSavedMessages(file: fileReference) + } + })) + ) + } + + subActions.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Files, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + + if let self { + let disposable: MetaDisposable + if let current = self.saveMediaDisposable { + disposable = current + } else { + disposable = MetaDisposable() + self.saveMediaDisposable = disposable + } + disposable.set( + saveMediaToFiles(context: context, fileReference: fileReference, present: { [weak self] c, a in + if let self, let controller = (self.getParentController() as? OverlayAudioPlayerControllerImpl) { + controller.present(c, in: .window(.root), with: a) + } + }) + ) + } + })) + ) + + let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil + subActions.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Info, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: noAction)) + ) + + c?.pushItems(items: .single(ContextController.Items(content: .list(subActions)))) + } + })) + ) + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveToFiles, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + f(.default) + + if let self { + let disposable: MetaDisposable + if let current = self.saveMediaDisposable { + disposable = current + } else { + disposable = MetaDisposable() + self.saveMediaDisposable = disposable + } + disposable.set( + saveMediaToFiles(context: context, fileReference: fileReference, present: { [weak self] c, a in + if let self, let controller = (self.getParentController() as? OverlayAudioPlayerControllerImpl) { + controller.present(c, in: .window(.root), with: a) + } + }) + ) + } + }))) + } + + var addedSeparator = false + + if message.id.namespace == Namespaces.Message.Cloud { + if !addedSeparator { + items.append(.separator) + addedSeparator = true + } + items.append( + .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_ShowInChat, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + guard let self else { + return + } + context.sharedContext.navigateToChat(accountId: context.account.id, peerId: message.id.peerId, messageId: message.id) + self.requestDismiss() + })) + ) + } + + // items.append( + // .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_Forward, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + // f(.default) + // + // if let _ = self { + // + // } + // })) + // ) + + var canDelete = false + if case .custom = self.source { + if self.savedIds?.contains(file.fileId.id) == true { + canDelete = true + } + } else if let peer = message.peers[message.id.peerId] { + if peer is TelegramUser || peer is TelegramSecretChat { + canDelete = true + } else if let _ = peer as? TelegramGroup { + canDelete = true + } else if let channel = peer as? TelegramChannel { + if message.flags.contains(.Incoming) { + canDelete = channel.hasPermission(.deleteAllMessages) + } else { + canDelete = true + } + } else { + canDelete = false + } + } else { + canDelete = false + } + + if canDelete { + if !addedSeparator { + items.append(.separator) + addedSeparator = true + } + var actionTitle = presentationData.strings.MediaPlayer_ContextMenu_Delete + if case .custom = self.source { + actionTitle = presentationData.strings.MediaPlayer_ContextMenu_Remove + } + items.append( + .action(ContextMenuActionItem(text: actionTitle, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in + guard let self else { + return + } + if message.id.namespace == Namespaces.Message.Local { + f(.default) + self.removeFromSavedMusic(file: fileReference) + } else { + c?.setItems(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)) + |> map { peer -> ContextController.Items in + var items: [ContextMenuItem] = [] + let messageIds = [message.id] + + if let peer { + var personalPeerName: String? + var isChannel = false + if case let .user(user) = peer { + personalPeerName = EnginePeer(user).compactDisplayTitle + } else if case let .channel(channel) = peer, case .broadcast = channel.info { + isChannel = true + } + + if actions.options.contains(.deleteGlobally) { + let globalTitle: String + if isChannel { + globalTitle = presentationData.strings.Conversation_DeleteMessagesForEveryone + } else if let personalPeerName = personalPeerName { + globalTitle = presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string + } else { + globalTitle = presentationData.strings.Conversation_DeleteMessagesForEveryone + } + items.append(.action(ContextMenuActionItem(text: globalTitle, textColor: .destructive, icon: { _ in nil }, action: { c, f in + c?.dismiss(completion: { + let _ = context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + }) + }))) + } + + if actions.options.contains(.deleteLocally) { + var localOptionText = presentationData.strings.Conversation_DeleteMessagesForMe + if context.account.peerId == message.id.peerId { + if messageIds.count == 1 { + localOptionText = presentationData.strings.Conversation_Moderate_Delete + } else { + localOptionText = presentationData.strings.Conversation_DeleteManyMessages + } + } + items.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { c, f in + c?.dismiss(completion: { + let _ = context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).startStandalone() + }) + }))) + } + } + + return ContextController.Items(content: .list(items)) + }, minHeight: nil, animated: true) + } + })) + ) + } + + guard !items.isEmpty else { + return + } + + let contextController = ContextController(presentationData: presentationData, source: source, items: .single(ContextController.Items(content: .list(items))), recognizer: recognizer, gesture: gesture) + self.getParentController()?.presentInGlobalOverlay(contextController) + }) + } +} + +private final class OverlayAudioPlayerContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = false + let ignoreContentTouches: Bool = false + let blurBackground: Bool = true + let additionalInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 80.0, right: 0.0) + + private let contentNode: ContextExtractedContentContainingNode + + init(contentNode: ContextExtractedContentContainingNode) { + self.contentNode = contentNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.contentNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } } diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 8a999cf103..6f8141ddb2 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -19,6 +19,11 @@ import ContextUI import SliderContextItem import UndoUI import MarqueeComponent +import MultilineTextComponent +import BundleIconComponent +import ButtonComponent +import Markdown +import TextFormat private func normalizeValue(_ value: CGFloat) -> CGFloat { return round(value * 10.0) / 10.0 @@ -106,8 +111,8 @@ private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat { return size.width } -private let titleFont = Font.semibold(18.0) -private let descriptionFont = Font.regular(18.0) +private let titleFont = Font.semibold(19.0) +private let descriptionFont = Font.regular(17.0) private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?, Bool, NSAttributedString?) { var titleString: NSAttributedString? @@ -141,6 +146,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private let account: Account private let engine: TelegramEngine private var presentationData: PresentationData + private let chatLocation: ChatLocation + private let source: ChatHistoryListSource private let backgroundNode: ASImageNode @@ -154,6 +161,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private let shareNode: HighlightableButtonNode private let artistButton: HighlightTrackingButtonNode + private var profileAudio: ComponentView? + private var cachedChevronImage: (UIImage, PresentationTheme)? + private let scrubberNode: MediaPlayerScrubbingNode private let leftDurationLabel: MediaPlayerTimeTextNode private let rightDurationLabel: MediaPlayerTimeTextNode @@ -181,12 +191,18 @@ final class OverlayPlayerControlsNode: ASDisplayNode { let separatorNode: ASDisplayNode + private let sectionBackground: ASDisplayNode + private let sectionTitle: ComponentView + var isExpanded = false var updateIsExpanded: (() -> Void)? var requestCollapse: (() -> Void)? - var requestShare: ((MessageId) -> Void)? + var requestShare: ((ShareControllerSubject) -> Void)? var requestSearchByArtist: ((String) -> Void)? + var requestSaveToProfile: ((FileMediaReference) -> Void)? + var requestRemoveFromProfile: ((FileMediaReference) -> Void)? + var requestLayout: ((ContainedViewLayoutTransition) -> Void)? var updateOrder: ((MusicPlaybackSettingsOrder) -> Void)? var control: ((SharedMediaPlayerControlAction) -> Void)? @@ -197,10 +213,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private var displayData: SharedMediaPlaybackDisplayData? private var currentAlbumArtInitialized = false private var currentAlbumArt: SharedMediaPlaybackAlbumArt? - private var currentFileReference: FileMediaReference? + private(set) var currentFileReference: FileMediaReference? private var statusDisposable: Disposable? private var chapterDisposable: Disposable? + private var peerName: String? + private var peerDisposable: Disposable? + private var previousCaption: NSAttributedString? private var chaptersPromise = ValuePromise<[MediaPlayerScrubbingChapter]>([]) private var currentChapter: MediaPlayerScrubbingChapter? @@ -215,13 +234,15 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private var currentDuration: Double = 0.0 private var currentPosition: Double = 0.0 - private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat)? + private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, hasSectionHeader: Bool, savedMusic: Bool?)? - init(account: Account, engine: TelegramEngine, accountManager: AccountManager, presentationData: PresentationData, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError>) { + init(account: Account, engine: TelegramEngine, accountManager: AccountManager, presentationData: PresentationData, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError>, chatLocation: ChatLocation, source: ChatHistoryListSource) { self.accountManager = accountManager self.account = account self.engine = engine self.presentationData = presentationData + self.chatLocation = chatLocation + self.source = source self.backgroundNode = ASImageNode() self.backgroundNode.isLayerBacked = true @@ -240,7 +261,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.titleNode.displaysAsynchronously = false self.title = ComponentView() - + self.descriptionNode = TextNode() self.descriptionNode.isUserInteractionEnabled = false self.descriptionNode.displaysAsynchronously = false @@ -293,6 +314,11 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.separatorNode.isLayerBacked = true self.separatorNode.backgroundColor = presentationData.theme.list.itemPlainSeparatorColor + self.sectionBackground = ASDisplayNode() + self.sectionBackground.backgroundColor = presentationData.theme.chatList.sectionHeaderFillColor + + self.sectionTitle = ComponentView() + super.init() self.addSubnode(self.backgroundNode) @@ -318,6 +344,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.addSubnode(self.playPauseButton) self.playPauseButton.addSubnode(self.playPauseIconNode) + self.addSubnode(self.sectionBackground) self.addSubnode(self.separatorNode) let accountId = account.id @@ -369,7 +396,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { strongSelf.infoNodePushed = infoNodePushed if let layout = strongSelf.validLayout { - let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, transition: .animated(duration: 0.35, curve: .spring)) + let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .animated(duration: 0.35, curve: .spring)) } } }) @@ -379,6 +406,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { guard let strongSelf = self else { return } + var itemUpdated = false var valueItemId: SharedMediaPlaylistItemId? if let (_, value, _) = value, case let .state(state) = value { valueItemId = state.item.id @@ -386,6 +414,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if !areSharedMediaPlaylistItemIdsEqual(valueItemId, strongSelf.currentItemId) { strongSelf.currentItemId = valueItemId strongSelf.scrubberNode.ignoreSeekId = nil + itemUpdated = true } var rateButtonIsHidden = true @@ -445,7 +474,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if duration != strongSelf.currentDuration && !duration.isZero { strongSelf.currentDuration = duration if let layout = strongSelf.validLayout { - let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, transition: .immediate) + let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate) } } @@ -485,8 +514,25 @@ final class OverlayPlayerControlsNode: ASDisplayNode { strongSelf.shareNode.isHidden = !canShare } + + if itemUpdated { + strongSelf.requestLayout?(.animated(duration: 0.2, curve: .easeInOut)) + } }) + if case .custom = self.source, case let .peer(peerId) = self.chatLocation, peerId != account.peerId { + self.peerDisposable = (engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + self.peerName = peer.compactDisplayTitle + if let layout = self.validLayout { + let _ = self.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate) + } + }) + } + self.chapterDisposable = combineLatest(queue: Queue.mainQueue(), mappedStatus, self.chaptersPromise.get()) .startStrict(next: { [weak self] status, chapters in if let strongSelf = self, status.duration > 1.0, chapters.count > 0 { @@ -533,7 +579,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { strongSelf.infoNode.attributedText = NSAttributedString(string: chapter.title, font: Font.regular(13.0), textColor: strongSelf.presentationData.theme.list.itemSecondaryTextColor) if let layout = strongSelf.validLayout { - let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, transition: .immediate) + let _ = strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, hasSectionHeader: layout.4, savedMusic: layout.5, transition: .immediate) } } } @@ -578,6 +624,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.statusDisposable?.dispose() self.chapterDisposable?.dispose() self.scrubbingDisposable?.dispose() + self.peerDisposable?.dispose() } override func didLoad() { @@ -704,14 +751,15 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.updateRateButton(rate) } self.separatorNode.backgroundColor = presentationData.theme.list.itemPlainSeparatorColor + self.sectionBackground.backgroundColor = presentationData.theme.chatList.sectionHeaderFillColor } private func updateLabels(transition: ContainedViewLayoutTransition) { - guard let (width, leftInset, rightInset, maxHeight) = self.validLayout else { + guard let (width, leftInset, rightInset, maxHeight, _, _) = self.validLayout else { return } - let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded) + let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: false, savedMusic: nil) let sideInset: CGFloat = 20.0 @@ -740,13 +788,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode { MarqueeComponent(attributedText: titleString ?? NSAttributedString()) ), environment: {}, - containerSize: CGSize(width: width - sideInset * 2.0 - leftInset - rightInset - infoLabelsLeftInset - infoLabelsRightInset + MarqueeComponent.innerPadding, height: CGFloat.greatestFiniteMagnitude) + containerSize: CGSize(width: width - sideInset * 2.0 - leftInset - rightInset - infoLabelsLeftInset - infoLabelsRightInset, height: CGFloat.greatestFiniteMagnitude) ) if let titleView = self.title.view { if titleView.superview == nil { self.view.addSubview(titleView) } - transition.updateFrame(view: titleView, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - titleSize.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset) - MarqueeComponent.innerPadding, y: infoVerticalOrigin + 1.0), size: titleSize)) + transition.updateFrame(view: titleView, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - titleSize.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin), size: titleSize)) } let makeDescriptionLayout = TextNode.asyncLayout(self.descriptionNode) @@ -755,7 +803,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - titleLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 1.0), size: titleLayout.size)) let _ = titleApply() - let descriptionFrame = CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 24.0), size: descriptionLayout.size) + let descriptionFrame = CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 25.0), size: descriptionLayout.size) transition.updateFrame(node: self.descriptionNode, frame: descriptionFrame) let _ = descriptionApply() @@ -819,20 +867,30 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } static let basePanelHeight: CGFloat = 220.0 + static let sectionHeaderHeight: CGFloat = 28.0 - static func heightForLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, isExpanded: Bool) -> CGFloat { + static func heightForLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, isExpanded: Bool, hasSectionHeader: Bool, savedMusic: Bool?) -> CGFloat { var panelHeight: CGFloat = OverlayPlayerControlsNode.basePanelHeight if isExpanded { let sideInset: CGFloat = 20.0 panelHeight += width - leftInset - rightInset - sideInset * 2.0 + 24.0 } - return min(panelHeight, maxHeight) + var height = min(panelHeight, maxHeight) + if hasSectionHeader { + height += sectionHeaderHeight + } + if let savedMusic { + height += savedMusic ? 38.0 : 70.0 + } + return height } - func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - self.validLayout = (width, leftInset, rightInset, maxHeight) + func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, hasSectionHeader: Bool, savedMusic: Bool?, transition: ContainedViewLayoutTransition) -> CGFloat { + let previousSavedMusic = self.validLayout?.savedMusic + self.validLayout = (width, leftInset, rightInset, maxHeight, hasSectionHeader, savedMusic) - let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded) + let finalPanelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: hasSectionHeader, savedMusic: savedMusic) + let panelHeight = OverlayPlayerControlsNode.heightForLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isExpanded: self.isExpanded, hasSectionHeader: false, savedMusic: nil) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight), size: CGSize(width: width, height: UIScreenPixel))) @@ -841,6 +899,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { let sideInset: CGFloat = 20.0 let sideButtonsInset: CGFloat = sideInset + 36.0 + let infoVerticalOrigin: CGFloat = panelHeight - OverlayPlayerControlsNode.basePanelHeight + 36.0 self.updateLabels(transition: transition) @@ -849,7 +908,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { let albumArtSize = CGSize(width: 48.0, height: 48.0) let makeAlbumArtLayout = self.albumArtNode.asyncLayout() - let applyAlbumArt = makeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: albumArtSize, boundingSize: albumArtSize, intrinsicInsets: UIEdgeInsets())) + let applyAlbumArt = makeAlbumArtLayout(TransformImageArguments(corners: ImageCorners(radius: 10.0), imageSize: albumArtSize, boundingSize: albumArtSize, intrinsicInsets: UIEdgeInsets())) applyAlbumArt() let albumArtFrame = CGRect(origin: CGPoint(x: leftInset + sideInset, y: infoVerticalOrigin - 1.0), size: albumArtSize) let previousAlbumArtNodeFrame = self.albumArtNode.frame @@ -941,7 +1000,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { let rateRightOffset = timestampLabelWidthForDuration(self.currentDuration) transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: width - sideInset - rightInset - rateRightOffset - 28.0, y: scrubberVerticalOrigin + 10.0 + rightLabelVerticalOffset - 10.0), size: CGSize(width: 24.0, height: 44.0))) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: width, height: panelHeight + 8.0))) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -8.0), size: CGSize(width: width, height: finalPanelHeight + 8.0))) let buttonSize = CGSize(width: 64.0, height: 64.0) let buttonsWidth = min(width - leftInset - rightInset - sideButtonsInset * 2.0, 320.0) @@ -956,8 +1015,162 @@ final class OverlayPlayerControlsNode: ASDisplayNode { let playPauseFrame = CGRect(origin: CGPoint(x: buttonsRect.minX + floor((buttonsRect.width - buttonSize.width) / 2.0), y: buttonsRect.minY), size: buttonSize) transition.updateFrame(node: self.playPauseButton, frame: playPauseFrame) transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: -6.0, y: -6.0), size: CGSize(width: 76.0, height: 76.0))) + + var sectionHeaderTransition = transition + if self.sectionTitle.view?.superview == nil { + sectionHeaderTransition = .immediate + } - return panelHeight + sectionHeaderTransition.updateFrame(node: self.sectionBackground, frame: CGRect(origin: CGPoint(x: 0.0, y: finalPanelHeight - OverlayPlayerControlsNode.sectionHeaderHeight), size: CGSize(width: width, height: OverlayPlayerControlsNode.sectionHeaderHeight))) + + self.separatorNode.isHidden = hasSectionHeader + + if hasSectionHeader { + let sideInset: CGFloat = 16.0 + var sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_ThisChat + if let peerName = self.peerName { + sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusic(peerName.uppercased()).string + } else if case .custom = self.source { + sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusicYou + } + let sectionTitleSize = self.sectionTitle.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString(string: sectionTitle, font: Font.regular(13.0), textColor: self.presentationData.theme.chatList.sectionHeaderTextColor)), + truncationType: .middle + ) + ), + environment: {}, + containerSize: CGSize(width: width - leftInset - rightInset - sideInset * 2.0, height: OverlayPlayerControlsNode.sectionHeaderHeight) + ) + if let sectionTitleView = self.sectionTitle.view { + if sectionTitleView.superview == nil { + self.view.addSubview(sectionTitleView) + } + sectionTitleView.bounds = CGRect(origin: .zero, size: sectionTitleSize) + sectionHeaderTransition.updateFrame(view: sectionTitleView, frame: CGRect(origin: CGPoint(x: leftInset + sideInset, y: finalPanelHeight - OverlayPlayerControlsNode.sectionHeaderHeight + 6.0 + UIScreenPixel), size: sectionTitleSize)) + } + } else if let sectionTitleView = self.sectionTitle.view, sectionTitleView.superview != nil { + sectionTitleView.removeFromSuperview() + } + + if let savedMusic { + var profileAudioTransition = transition + var animateIn = false + if previousSavedMusic != savedMusic, let profileAudio { + self.profileAudio = nil + if let profileAudioView = profileAudio.view { + if transition.isAnimated { + profileAudioView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.25) + profileAudioView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + profileAudioView.removeFromSuperview() + }) + animateIn = true + } else { + profileAudioView.removeFromSuperview() + } + } + } + + if self.profileAudio == nil { + profileAudioTransition = .immediate + } + let profileAudio: ComponentView = self.profileAudio ?? { + let componentView = ComponentView() + self.profileAudio = componentView + return componentView + }() + + let profileAudioComponent: AnyComponent + var profileAudioOffset: CGFloat = 0.0 + if savedMusic { + if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== self.presentationData.theme { + self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: self.presentationData.theme.list.itemAccentColor)!, self.presentationData.theme) + } + let textFont = Font.regular(13.0) + let textColor = self.presentationData.theme.list.itemSecondaryTextColor + let linkColor = self.presentationData.theme.list.itemAccentColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + let attributedString = parseMarkdownIntoAttributedString(self.presentationData.strings.MediaPlayer_SavedMusic_RemoveFromProfile, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString + if let range = attributedString.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 { + attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string)) + } + profileAudioComponent = AnyComponent(MultilineTextComponent( + text: .plain(attributedString), + horizontalAlignment: .center, + maximumNumberOfLines: 5, + lineSpacing: 0.2, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { [weak self] attributes, _ in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + if let file = self?.currentFileReference { + self?.requestRemoveFromProfile?(file) + } + } + } + )) + profileAudioOffset = 18.0 + } else { + profileAudioComponent = AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + color: self.presentationData.theme.list.itemCheckColors.fillColor, + foreground: self.presentationData.theme.list.itemCheckColors.foregroundColor, + pressedColor: self.presentationData.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), + cornerRadius: 10.0 + ), + content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "icon", component: AnyComponent( + BundleIconComponent(name: "Peer Info/SaveMusic", tintColor: self.presentationData.theme.list.itemCheckColors.foregroundColor) + )), + AnyComponentWithIdentity(id: "label", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: self.presentationData.strings.MediaPlayer_SavedMusic_AddToProfile, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor))) + )) + ], spacing: 8.0) + )), + action: { [weak self] in + if let file = self?.currentFileReference { + self?.requestSaveToProfile?(file) + } + } + )) + } + + let profileAudioSize = profileAudio.update( + transition: .immediate, + component: profileAudioComponent, + environment: {}, + containerSize: CGSize(width: width - leftInset - rightInset - 32.0, height: 50.0) + ) + let profileAudioOrigin: CGFloat = finalPanelHeight + profileAudioOffset - (hasSectionHeader ? OverlayPlayerControlsNode.sectionHeaderHeight : 0.0) - 42.0 - floorToScreenPixels(profileAudioSize.height / 2.0) + let profileAudioFrame = CGRect(origin: CGPoint(x: floor((width - profileAudioSize.width) / 2.0), y: profileAudioOrigin), size: profileAudioSize) + if let profileAudioView = profileAudio.view { + if profileAudioView.superview == nil { + self.view.addSubview(profileAudioView) + + if animateIn { + profileAudioView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + profileAudioView.layer.animateScale(from: 0.0, to: 1.0, duration: 0.3) + } + } + profileAudioTransition.updateFrame(view: profileAudioView, frame: profileAudioFrame) + } + } + + return finalPanelHeight } func collapse() { @@ -973,7 +1186,17 @@ final class OverlayPlayerControlsNode: ASDisplayNode { @objc func sharePressed() { if let itemId = self.currentItemId as? PeerMessagesMediaPlaylistItemId { - self.requestShare?(itemId.messageId) + if itemId.messageId.namespace == Namespaces.Message.Cloud { + let _ = (self.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: itemId.messageId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] message in + guard let message else { + return + } + self?.requestShare?(.messages([message._asMessage()])) + }) + } else if let fileReference = self.currentFileReference { + self.requestShare?(.media(fileReference.abstract, nil)) + } } } diff --git a/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift b/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift index 386c91c9ca..67e24534ad 100644 --- a/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift +++ b/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift @@ -9,7 +9,7 @@ import ChatControllerInteraction import ChatHistoryEntry import ChatMessageBubbleItemNode -func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, scrollAnimationCurve: ListViewAnimationCurve?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool, messageTransitionNode: ChatMessageTransitionNodeImpl?, allUpdated: Bool) -> ChatHistoryViewTransition { +func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, source: ChatHistoryListSource, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, scrollAnimationCurve: ListViewAnimationCurve?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool, messageTransitionNode: ChatMessageTransitionNodeImpl?, allUpdated: Bool) -> ChatHistoryViewTransition { var mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)]) let allUpdated = allUpdated || (fromView?.associatedData != toView.associatedData) if reverse { @@ -141,6 +141,11 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie let curve: ListViewAnimationCurve = scrollAnimationCurve ?? .Default(duration: nil) + var isSavedMusic = false + if case let .custom(_, _, _, isSavedMusicValue, _, _) = source { + isSavedMusic = isSavedMusicValue + } + if let scrollPosition = scrollPosition { switch scrollPosition { case let .unread(unreadIndex): @@ -238,9 +243,17 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie } var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { - if scrollIndex.index.isLessOrEqual(to: entry.index) { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint, displayLink: displayLink) - break + if isSavedMusic { + if case let .message(messageIndex) = scrollIndex.index, messageIndex.id == entry.index.id { + print(messageIndex.id) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint, displayLink: displayLink) + break + } + } else { + if scrollIndex.index.isLessOrEqual(to: entry.index) { + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint, displayLink: displayLink) + break + } } index -= 1 } diff --git a/submodules/TelegramUI/Sources/SaveMediaToFiles.swift b/submodules/TelegramUI/Sources/SaveMediaToFiles.swift new file mode 100644 index 0000000000..7ad5919923 --- /dev/null +++ b/submodules/TelegramUI/Sources/SaveMediaToFiles.swift @@ -0,0 +1,128 @@ +import Foundation +import AVFoundation +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import AccountContext +import OverlayStatusController +import LegacyMediaPickerUI +import SaveToCameraRoll +import PresentationDataUtils + +func saveMediaToFiles(context: AccountContext, fileReference: FileMediaReference, present: @escaping (ViewController, Any?) -> Void) -> Disposable { + var title: String? + var performer: String? + for attribute in fileReference.media.attributes { + if case let .Audio(_, _, titleValue, performerValue, _) = attribute { + if let titleValue, !titleValue.isEmpty { + title = titleValue + } + if let performerValue, !performerValue.isEmpty { + performer = performerValue + } + } + } + + var signal = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: fileReference.abstract) + + var cancelImpl: (() -> Void)? + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let progressSignal = Signal { subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.15, queue: Queue.mainQueue()) + + let progressDisposable = progressSignal.startStrict() + + let disposable = MetaDisposable() + signal = signal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { [weak disposable] in + disposable?.set(nil) + } + disposable.set((signal + |> deliverOnMainQueue).startStrict(next: { state, _ in + switch state { + case .progress: + break + case let .data(data): + if data.complete { + var symlinkPath = data.path + ".mp3" + if fileSize(symlinkPath) != nil { + try? FileManager.default.removeItem(atPath: symlinkPath) + } + let _ = try? FileManager.default.linkItem(atPath: data.path, toPath: symlinkPath) + + let audioUrl = URL(fileURLWithPath: symlinkPath) + let audioAsset = AVURLAsset(url: audioUrl) + + var fileExtension = "mp3" + if let filename = fileReference.media.fileName { + if let dotIndex = filename.lastIndex(of: ".") { + fileExtension = String(filename[filename.index(after: dotIndex)...]) + } + } + + var nameComponents: [String] = [] + if let title { + if let performer { + nameComponents.append(performer) + } + nameComponents.append(title) + } else { + var artist: String? + var title: String? + for data in audioAsset.commonMetadata { + if data.commonKey == .commonKeyArtist { + artist = data.stringValue + } + if data.commonKey == .commonKeyTitle { + title = data.stringValue + } + } + if let artist, !artist.isEmpty { + nameComponents.append(artist) + } + if let title, !title.isEmpty { + nameComponents.append(title) + } + if nameComponents.isEmpty, var filename = fileReference.media.fileName { + if let dotIndex = filename.lastIndex(of: ".") { + filename = String(filename[.. Void)?, dismissed: (() -> Void)?) -> ViewController { - let controller = GiftViewScreen(context: context, subject: .uniqueGift(gift, nil), shareStory: shareStory) + public func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController { + let controller = GiftViewScreen(context: context, subject: .uniqueGift(gift, nil), shareStory: shareStory, openChatTheme: openChatTheme) controller.disposed = { dismissed?() } diff --git a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift index 709dfaeab8..7d48f091fa 100644 --- a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift +++ b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift @@ -9,6 +9,7 @@ import TelegramAudio import AccountContext import TelegramUniversalVideoContent import DeviceProximity +import PeerMessagesMediaPlaylist private enum SharedMediaPlaybackItem: Equatable { case audio(MediaPlayer) diff --git a/submodules/TelegramUI/Sources/SharedWakeupManager.swift b/submodules/TelegramUI/Sources/SharedWakeupManager.swift index 7b4c2e5731..3f857c200d 100644 --- a/submodules/TelegramUI/Sources/SharedWakeupManager.swift +++ b/submodules/TelegramUI/Sources/SharedWakeupManager.swift @@ -1,10 +1,13 @@ import Foundation +import AVFAudio import UIKit import SwiftSignalKit import Postbox import TelegramCore import TelegramCallsUI import AccountContext +import UniversalMediaPlayer +import TelegramAudio private struct AccountTasks { let stateSynchronization: Bool @@ -68,6 +71,7 @@ public final class SharedWakeupManager { private var managedPausedInBackgroundPlayer: Disposable? private var keepIdleDisposable: Disposable? + private var silenceAudioRenderer: MediaPlayerAudioRenderer? private var accountsAndTasks: [(Account, Bool, AccountTasks)] = [] @@ -425,10 +429,42 @@ public final class SharedWakeupManager { keepIdleDisposable.dispose() } } + + if !self.inForeground && hasPendingMessages && !self.hasActiveAudioSession { + if self.silenceAudioRenderer == nil { + let audioSession = AVAudioSession() + let _ = try? audioSession.setCategory(.ambient) + let _ = try? audioSession.setMode(.default) + let silenceAudioRenderer = MediaPlayerAudioRenderer( + audioSession: .custom({ control in + let _ = try? audioSession.setActive(true) + control.activate() + + return EmptyDisposable + }), + forAudioVideoMessage: false, + playAndRecord: false, + useVoiceProcessingMode: false, + soundMuted: false, + ambient: true, + mixWithOthers: true, + forceAudioToSpeaker: false, + baseRate: 1.0, + audioLevelPipe: ValuePipe(), + updatedRate: {}, + audioPaused: {} + ) + self.silenceAudioRenderer = silenceAudioRenderer + silenceAudioRenderer.start() + } + } else if let silenceAudioRenderer = self.silenceAudioRenderer { + self.silenceAudioRenderer = nil + silenceAudioRenderer.stop() + } } private func updateAccounts(hasTasks: Bool, endTaskAfterTransactionsComplete: UIBackgroundTaskIdentifier?) { - if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension || (hasTasks && self.currentExternalCompletion != nil) || self.activeExplicitExtensionTimer != nil { + if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension || (hasTasks && self.currentExternalCompletion != nil) || self.activeExplicitExtensionTimer != nil || self.silenceAudioRenderer != nil { Logger.shared.log("Wakeup", "enableBeginTransactions: true (active)") for (account, primary, tasks) in self.accountsAndTasks { diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 23ab2794e3..b20037d083 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -203,6 +203,14 @@ public struct OngoingCallContextState: Equatable { public let remoteVideoState: RemoteVideoState public let remoteAudioState: RemoteAudioState public let remoteBatteryLevel: RemoteBatteryLevel + + public init(state: State, videoState: VideoState, remoteVideoState: RemoteVideoState, remoteAudioState: RemoteAudioState, remoteBatteryLevel: RemoteBatteryLevel) { + self.state = state + self.videoState = videoState + self.remoteVideoState = remoteVideoState + self.remoteAudioState = remoteAudioState + self.remoteBatteryLevel = remoteBatteryLevel + } } private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue, OngoingCallThreadLocalContextQueueWebrtc { diff --git a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift index b2537ee679..55f1cc2c25 100644 --- a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift +++ b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift @@ -80,7 +80,7 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M private let syntaxHighlighter = Syntaxer() -public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseQuoteSecondaryTintColor: UIColor? = nil, baseQuoteTertiaryTintColor: UIColor? = nil, codeBlockTitleColor: UIColor? = nil, codeBlockAccentColor: UIColor? = nil, codeBlockBackgroundColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:], adjustQuoteFontSize: Bool = false, cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil) -> NSAttributedString { +public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseQuoteSecondaryTintColor: UIColor? = nil, baseQuoteTertiaryTintColor: UIColor? = nil, codeBlockTitleColor: UIColor? = nil, codeBlockAccentColor: UIColor? = nil, codeBlockBackgroundColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:], adjustQuoteFontSize: Bool = false, cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil, paragraphAlignment: NSTextAlignment? = nil) -> NSAttributedString { let baseQuoteTintColor = baseQuoteTintColor ?? baseColor var nsString: NSString? @@ -356,6 +356,12 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti } }) + if let paragraphAlignment = paragraphAlignment { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = paragraphAlignment + string.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, string.length)) + } + return string } diff --git a/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift deleted file mode 100644 index 8b13789179..0000000000 --- a/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift +++ /dev/null @@ -1 +0,0 @@ - diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 0e69daa3c4..4bfe3ee995 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -8,6 +8,7 @@ import TelegramCore import AccountContext import SwiftSignalKit import WallpaperResources +import StickerResources import FastBlur import Svg import GZip @@ -85,6 +86,7 @@ public protocol WallpaperBackgroundNode: ASDisplayNode { var rotation: CGFloat { get set } func update(wallpaper: TelegramWallpaper, animated: Bool) + func update(wallpaper: TelegramWallpaper, starGift: StarGift?, animated: Bool) func _internalUpdateIsSettingUpWallpaper() func updateLayout(size: CGSize, displayMode: WallpaperDisplayMode, transition: ContainedViewLayoutTransition) func updateIsLooping(_ isLooping: Bool) @@ -758,11 +760,20 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou private var validLayout: (CGSize, WallpaperDisplayMode)? private var wallpaper: TelegramWallpaper? + private var starGift: StarGift? + private var modelRectIndex: Int32? + + private var modelStickerNode: DefaultAnimatedStickerNodeImpl? + private var isSettingUpWallpaper: Bool = false private struct CachedValidPatternImage { let generate: (TransformImageArguments) -> DrawingContext? let generated: ValidPatternGeneratedImage + let rects: [WallpaperGiftPatternRect] + let starGift: StarGift? + let symbolImage: UIImage? + let modelRectIndex: Int32? let image: UIImage } @@ -771,6 +782,10 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou private struct ValidPatternImage { let wallpaper: TelegramWallpaper let invertPattern: Bool + let rects: [WallpaperGiftPatternRect] + let starGift: StarGift? + let symbolImage: UIImage? + let modelRectIndex: Int32? let generate: (TransformImageArguments) -> DrawingContext? } private var validPatternImage: ValidPatternImage? @@ -781,10 +796,38 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou let patternColor: UInt32 let backgroundColor: UInt32 let invertPattern: Bool + let starGift: StarGift? + let modelRectIndex: Int32? + + public static func ==(lhs: ValidPatternGeneratedImage, rhs: ValidPatternGeneratedImage) -> Bool { + if lhs.wallpaper != rhs.wallpaper { + return false + } + if lhs.size != rhs.size { + return false + } + if lhs.patternColor != rhs.patternColor { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.invertPattern != rhs.invertPattern { + return false + } + if lhs.starGift?.slug != rhs.starGift?.slug { + return false + } + if lhs.modelRectIndex != rhs.modelRectIndex { + return false + } + return true + } } private var validPatternGeneratedImage: ValidPatternGeneratedImage? private let patternImageDisposable = MetaDisposable() + private let symbolImageDisposable = MetaDisposable() private var bubbleTheme: PresentationTheme? private var bubbleCorners: PresentationChatBubbleCorners? @@ -930,11 +973,26 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } public func update(wallpaper: TelegramWallpaper, animated: Bool) { - if self.wallpaper == wallpaper { + self.update(wallpaper: wallpaper, starGift: nil, animated: animated) + } + + public func update(wallpaper: TelegramWallpaper, starGift: StarGift?, animated: Bool) { + if self.wallpaper == wallpaper && self.starGift == starGift { return } let previousWallpaper = self.wallpaper + let previousStarGift = self.starGift + self.wallpaper = wallpaper + self.starGift = starGift + + if previousWallpaper != wallpaper || previousStarGift?.slug != starGift?.slug { + if let _ = starGift { + self.modelRectIndex = Int32.random(in: 0 ..< 10) + } else { + self.modelRectIndex = nil + } + } if let _ = previousWallpaper, animated { if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) { @@ -1132,6 +1190,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } default: self.patternImageDisposable.set(nil) + self.symbolImageDisposable.set(nil) self.validPatternImage = nil self.patternImageLayer.isHidden = true self.patternImageLayer.fillWithColorUntilLoaded = nil @@ -1146,6 +1205,9 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou guard let wallpaper = self.wallpaper else { return } + + let starGift = self.starGift + let modelRectIndex = self.modelRectIndex var invertPattern: Bool = false var patternIsLight: Bool = false @@ -1169,13 +1231,20 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou break } } + + if let previousStarGift = self.validPatternImage?.starGift, !updated { + updated = true + if previousStarGift.slug == starGift?.slug { + updated = false + } + } if updated { self.validPatternGeneratedImage = nil self.validPatternImage = nil - if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper && cachedValidPatternImage.generated.invertPattern == invertPattern { - self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, invertPattern: invertPattern, generate: cachedValidPatternImage.generate) + if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper && cachedValidPatternImage.generated.invertPattern == invertPattern && cachedValidPatternImage.starGift == starGift && cachedValidPatternImage.modelRectIndex == modelRectIndex { + self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, invertPattern: invertPattern, rects: cachedValidPatternImage.rects, starGift: cachedValidPatternImage.starGift, symbolImage: cachedValidPatternImage.symbolImage, modelRectIndex: cachedValidPatternImage.modelRectIndex, generate: cachedValidPatternImage.generate) } else { func reference(for resource: EngineMediaResource, media: EngineMedia) -> MediaResourceReference { return .wallpaper(wallpaper: .slug(file.slug), resource: resource._asResource()) @@ -1189,37 +1258,33 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: reference(for: EngineMediaResource(file.file.resource), media: EngineMedia(file.file)))) let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) - self.patternImageDisposable.set((signal - |> deliverOnMainQueue).start(next: { [weak self] generator in - guard let strongSelf = self else { + var symbolImage: Signal = .single(nil) + if let starGift = self.starGift, case let .unique(uniqueGift) = starGift { + for attribute in uniqueGift.attributes { + if case let .pattern(_, file, _) = attribute, let dimensions = file.dimensions { + let size = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)) + symbolImage = chatMessageAnimatedSticker(postbox: self.context.account.postbox, userLocation: .other, file: file, small: false, size: size) + |> map { generator -> UIImage? in + return generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero))?.generateImage() + } + break + } + } + } + self.patternImageDisposable.set(combineLatest(queue: Queue.mainQueue(), signal, symbolImage).start(next: { [weak self] generator, symbolImage in + guard let self else { return } - - if let generator = generator { - /*generator = { arguments in - let scale = arguments.scale ?? UIScreenScale - let context = DrawingContext(size: arguments.drawingSize, scale: scale, clear: true) - - context.withFlippedContext { c in - if let path = getAppBundle().path(forResource: "PATTERN_static", ofType: "svg"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { - if let image = drawSvgImage(data, CGSize(width: arguments.drawingSize.width * scale, height: arguments.drawingSize.height * scale), .clear, .black, false) { - c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize)) - } - } - } - - return context - }*/ - - strongSelf.validPatternImage = ValidPatternImage(wallpaper: wallpaper, invertPattern: invertPattern, generate: generator) - strongSelf.validPatternGeneratedImage = nil - if let (size, displayMode) = strongSelf.validLayout { - strongSelf.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: .immediate) + if let (generator, rects) = generator { + self.validPatternImage = ValidPatternImage(wallpaper: wallpaper, invertPattern: invertPattern, rects: rects, starGift: starGift, symbolImage: symbolImage, modelRectIndex: modelRectIndex, generate: generator) + self.validPatternGeneratedImage = nil + if let (size, displayMode) = self.validLayout { + self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: .immediate) } else { - strongSelf._isReady.set(true) + self._isReady.set(true) } } else { - strongSelf._isReady.set(true) + self._isReady.set(true) } })) } @@ -1244,8 +1309,8 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.patternImageLayer.backgroundColor = nil } - let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb, invertPattern: invertPattern) - + let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb, invertPattern: invertPattern, starGift: starGift, modelRectIndex: modelRectIndex) + if self.validPatternGeneratedImage != updatedGeneratedImage { self.validPatternGeneratedImage = updatedGeneratedImage @@ -1256,7 +1321,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.patternImageLayer.suspendCompositionUpdates = false self.patternImageLayer.updateCompositionIfNeeded() } else { - let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false, displayMode: displayMode.argumentsDisplayMode), scale: min(2.0, UIScreenScale)) + let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false, displayMode: displayMode.argumentsDisplayMode, symbolImage: generateTintedImage(image: validPatternImage.symbolImage, color: .white), modelRectIndex: self.modelRectIndex), scale: min(2.0, UIScreenScale)) if self.useSharedAnimationPhase || self.patternImageLayer.contents == nil { if let drawingContext = validPatternImage.generate(patternArguments) { if let image = drawingContext.generateImage() { @@ -1267,7 +1332,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.patternImageLayer.updateCompositionIfNeeded() if self.useSharedAnimationPhase { - WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) + WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, rects: validPatternImage.rects, starGift: validPatternImage.starGift, symbolImage: validPatternImage.symbolImage, modelRectIndex: validPatternImage.modelRectIndex, image: image) } } else { self.updatePatternPresentation() @@ -1288,7 +1353,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou strongSelf.updatePatternPresentation() if let image = image, strongSelf.useSharedAnimationPhase { - WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) + WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, rects: validPatternImage.rects, starGift: validPatternImage.starGift, symbolImage: validPatternImage.symbolImage, modelRectIndex: validPatternImage.modelRectIndex, image: image) } } } @@ -1306,6 +1371,68 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.updatePatternPresentation() } } + + var modelFile: TelegramMediaFile? + if let validPatternImage = self.validPatternImage, !validPatternImage.rects.isEmpty, let starGift = validPatternImage.starGift { + if case let .unique(uniqueGift) = starGift { + for attribute in uniqueGift.attributes { + if case let .model(_, file, _) = attribute { + modelFile = file + } + } + } + } + if let validPatternImage = self.validPatternImage, !validPatternImage.rects.isEmpty, var modelRectIndex = self.modelRectIndex, let modelFile { + let filteredRects = validPatternImage.rects.filter { $0.center.y > $0.containerSize.height * 0.1 && $0.center.y < $0.containerSize.height * 0.9 } + modelRectIndex = modelRectIndex % Int32(filteredRects.count); + + let rect = filteredRects[Int(modelRectIndex)] + + let modelStickerNode: DefaultAnimatedStickerNodeImpl + if let current = self.modelStickerNode { + modelStickerNode = current + } else { + modelStickerNode = DefaultAnimatedStickerNodeImpl() + modelStickerNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: modelFile.resource, isVideo: false), width: 96, height: 96, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + modelStickerNode.visibility = true + self.modelStickerNode = modelStickerNode + self.addSubnode(modelStickerNode) + } + + let targetSize: CGSize = self.bounds.size + let containerSize: CGSize = rect.containerSize + let useAspectFit: Bool = false + + let renderScale: CGFloat = useAspectFit + ? min(targetSize.width / containerSize.width, targetSize.height / containerSize.height) + : max(targetSize.width / containerSize.width, targetSize.height / containerSize.height) + + let drawingSize = CGSize(width: containerSize.width * renderScale, height: containerSize.height * renderScale) + + let offsetX = (targetSize.width - drawingSize.width) * 0.5 + let offsetY = (targetSize.height - drawingSize.height) * 0.5 + + let onScreenCenter = CGPoint(x: offsetX + rect.center.x * renderScale, y: offsetY + rect.center.y * renderScale) + + let side = rect.side * rect.scale * renderScale + modelStickerNode.bounds = CGRect(origin: .zero, size: CGSize(width: side, height: side)) + modelStickerNode.position = onScreenCenter + modelStickerNode.updateLayout(size: modelStickerNode.bounds.size) + modelStickerNode.alpha = 0.5 + + modelStickerNode.layer.transform = CATransform3DMakeRotation(rect.rotation, 0, 0, 1) + } else { + if let modelStickerNode = self.modelStickerNode { + self.modelStickerNode = nil + if transition.isAnimated { + modelStickerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak modelStickerNode] _ in + modelStickerNode?.removeFromSupernode() + }) + } else { + modelStickerNode.removeFromSupernode() + } + } + } transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: size)) } @@ -1559,3 +1686,14 @@ private protocol WallpaperComponentView: AnyObject { public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode { return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) } + +private extension StarGift { + var slug: String? { + switch self { + case let .unique(uniqueGift): + return uniqueGift.slug + default: + return nil + } + } +} diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index 41f5be0378..588c1cc851 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -353,14 +353,18 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments { let customPatternColor: UIColor? let bakePatternAlpha: CGFloat let displayMode: DisplayMode + let symbolImage: UIImage? + let modelRectIndex: Int32? - public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0, displayMode: DisplayMode = .aspectFill) { + public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0, displayMode: DisplayMode = .aspectFill, symbolImage: UIImage? = nil, modelRectIndex: Int32? = nil) { self.colors = colors self.rotation = rotation self.customPatternColor = customPatternColor self.preview = preview self.bakePatternAlpha = bakePatternAlpha self.displayMode = displayMode + self.symbolImage = symbolImage + self.modelRectIndex = modelRectIndex } public func serialized() -> NSArray { @@ -373,6 +377,9 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments { array.add(NSNumber(value: self.preview)) array.add(NSNumber(value: Double(self.bakePatternAlpha))) array.add(NSNumber(value: self.displayMode.rawValue)) + if let symbolImage { + array.add(symbolImage) + } return array } } @@ -470,18 +477,34 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana } } -public func patternWallpaperImage(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { +public struct WallpaperGiftPatternRect: Equatable { + public let containerSize: CGSize + public let center: CGPoint + public let side: CGFloat + public let scale: CGFloat + public let rotation: CGFloat + + fileprivate init(containerSize: CGSize, rect: GiftPatternRect) { + self.containerSize = containerSize + self.center = rect.center + self.side = rect.side + self.scale = rect.scale + self.rotation = rect.rotation + } +} + +public func patternWallpaperImage(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false, forcePrepared: Bool = false) -> Signal<(generator: (TransformImageArguments) -> DrawingContext?, rects: [WallpaperGiftPatternRect])?, NoError> { return patternWallpaperDatas(account: account, accountManager: accountManager, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize) |> mapToSignal { fullSizeData, fullSizeComplete in if !autoFetchFullSize || fullSizeComplete { - return patternWallpaperImageInternal(fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode) + return patternWallpaperImageInternal(fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode, forcePrepared: forcePrepared) } else { return .single(nil) } } } -private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { +private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode, forcePrepared: Bool = false) -> Signal<(generator: (TransformImageArguments) -> DrawingContext?, rects: [WallpaperGiftPatternRect])?, NoError> { var prominent = false if case .thumbnail = mode { prominent = true @@ -491,7 +514,11 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete return .single((fullSizeData, fullSizeComplete)) |> map { fullSizeData, fullSizeComplete in - return { arguments in + var rects: [WallpaperGiftPatternRect] = [] + if let fullSizeData, let patternData = getGiftPatternData(fullSizeData) { + rects = patternData.rects.map { WallpaperGiftPatternRect(containerSize: patternData.size, rect: $0) } + } + return ({ arguments in var scale = scale if scale.isZero { scale = arguments.scale ?? UIScreenScale @@ -561,12 +588,12 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete var image: UIImage? if let fullSizeData = fullSizeData { if mode == .screen { - image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0, displayMode == .aspectFit) + image = renderPreparedImageWithSymbol(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0, displayMode == .aspectFit, customArguments.symbolImage, customArguments.modelRectIndex ?? -1) } else { image = UIImage(data: fullSizeData) } } - + if let customPatternColor = customArguments.customPatternColor, customPatternColor.alpha < 1.0 { patternIsInverted = true c.setBlendMode(.copy) @@ -674,7 +701,7 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete } else { return nil } - } + }, rects) } } @@ -1452,6 +1479,10 @@ public func themeIconImage(account: Account, accountManager: AccountManager mapToSignal { wallpaper in if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { @@ -1479,6 +1511,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager 0.3 arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white) } - - return patternWallpaperImage(account: account, accountManager: accountManager, representations: convertedPreviewRepresentations, mode: .thumbnail, autoFetchFullSize: true) - |> mapToSignal { generator -> Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> in + return patternWallpaperImage(account: account, accountManager: accountManager, representations: useFallback ? convertedRepresentations : convertedPreviewRepresentations, mode: useFallback ? .screen : .thumbnail, autoFetchFullSize: true) + |> mapToSignal { generatorAndRects -> Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> in let imageSize = CGSize(width: 148.0, height: 320.0) let imageArguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, custom: arguments) - let context = generator?(imageArguments) + let context = generatorAndRects?.generator(imageArguments) let image = context?.generateImage() if !file.settings.colors.isEmpty { @@ -1771,7 +1803,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager gift_; + int32 resale_count_; + int53 min_resale_star_count_; + string title_; + + availableGift(); + + availableGift(object_ptr &&gift_, int32 resale_count_, int53 min_resale_star_count_, string const &title_); + + static const std::int32_t ID = 850530502; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class availableGift; + +class availableGifts final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + array> gifts_; + + availableGifts(); + + explicit availableGifts(array> &&gifts_); + + static const std::int32_t ID = -1450341886; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class ReactionType; class availableReaction final : public Object { @@ -2589,13 +2630,12 @@ class call final : public Object { bool is_outgoing_; bool is_video_; object_ptr state_; - int32 group_call_id_; call(); - call(int32 id_, int53 user_id_, bool is_outgoing_, bool is_video_, object_ptr &&state_, int32 group_call_id_); + call(int32 id_, int53 user_id_, bool is_outgoing_, bool is_video_, object_ptr &&state_); - static const std::int32_t ID = -1643259734; + static const std::int32_t ID = 920360804; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -2674,19 +2714,19 @@ class callDiscardReasonHungUp final : public CallDiscardReason { void store(TlStorerToString &s, const char *field_name) const final; }; -class callDiscardReasonAllowGroupCall final : public CallDiscardReason { +class callDiscardReasonUpgradeToGroupCall final : public CallDiscardReason { std::int32_t get_id() const final { return ID; } public: - bytes encrypted_group_call_key_; + string invite_link_; - callDiscardReasonAllowGroupCall(); + callDiscardReasonUpgradeToGroupCall(); - explicit callDiscardReasonAllowGroupCall(bytes const &encrypted_group_call_key_); + explicit callDiscardReasonUpgradeToGroupCall(string const &invite_link_); - static const std::int32_t ID = -1357414443; + static const std::int32_t ID = 1254509319; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -2980,13 +3020,14 @@ class callStateReady final : public CallState { bytes encryption_key_; array emojis_; bool allow_p2p_; + bool is_group_call_supported_; string custom_parameters_; callStateReady(); - callStateReady(object_ptr &&protocol_, array> &&servers_, string const &config_, bytes const &encryption_key_, array &&emojis_, bool allow_p2p_, string const &custom_parameters_); + callStateReady(object_ptr &&protocol_, array> &&servers_, string const &config_, bytes const &encryption_key_, array &&emojis_, bool allow_p2p_, bool is_group_call_supported_, string const &custom_parameters_); - static const std::int32_t ID = 731619651; + static const std::int32_t ID = -281776921; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -3117,6 +3158,103 @@ class callbackQueryPayloadGame final : public CallbackQueryPayload { void store(TlStorerToString &s, const char *field_name) const final; }; +class CanPostStoryResult: public Object { + public: +}; + +class canPostStoryResultOk final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 story_count_; + + canPostStoryResultOk(); + + explicit canPostStoryResultOk(int32 story_count_); + + static const std::int32_t ID = 2083205610; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class canPostStoryResultPremiumNeeded final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + canPostStoryResultPremiumNeeded(); + + static const std::int32_t ID = 935130501; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class canPostStoryResultBoostNeeded final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + canPostStoryResultBoostNeeded(); + + static const std::int32_t ID = 80246195; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class canPostStoryResultActiveStoryLimitExceeded final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + canPostStoryResultActiveStoryLimitExceeded(); + + static const std::int32_t ID = -1640759002; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class canPostStoryResultWeeklyLimitExceeded final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 retry_after_; + + canPostStoryResultWeeklyLimitExceeded(); + + explicit canPostStoryResultWeeklyLimitExceeded(int32 retry_after_); + + static const std::int32_t ID = 552858605; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class canPostStoryResultMonthlyLimitExceeded final : public CanPostStoryResult { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 retry_after_; + + canPostStoryResultMonthlyLimitExceeded(); + + explicit canPostStoryResultMonthlyLimitExceeded(int32 retry_after_); + + static const std::int32_t ID = -784208562; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class CanSendMessageToUserResult: public Object { public: }; @@ -3180,100 +3318,6 @@ class canSendMessageToUserResultUserRestrictsNewChats final : public CanSendMess void store(TlStorerToString &s, const char *field_name) const final; }; -class CanSendStoryResult: public Object { - public: -}; - -class canSendStoryResultOk final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - - canSendStoryResultOk(); - - static const std::int32_t ID = 1346171133; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class canSendStoryResultPremiumNeeded final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - - canSendStoryResultPremiumNeeded(); - - static const std::int32_t ID = 1451220585; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class canSendStoryResultBoostNeeded final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - - canSendStoryResultBoostNeeded(); - - static const std::int32_t ID = -1637816017; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class canSendStoryResultActiveStoryLimitExceeded final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - - canSendStoryResultActiveStoryLimitExceeded(); - - static const std::int32_t ID = -1344689450; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class canSendStoryResultWeeklyLimitExceeded final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 retry_after_; - - canSendStoryResultWeeklyLimitExceeded(); - - explicit canSendStoryResultWeeklyLimitExceeded(int32 retry_after_); - - static const std::int32_t ID = 323068088; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class canSendStoryResultMonthlyLimitExceeded final : public CanSendStoryResult { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 retry_after_; - - canSendStoryResultMonthlyLimitExceeded(); - - explicit canSendStoryResultMonthlyLimitExceeded(int32 retry_after_); - - static const std::int32_t ID = -578665771; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - class CanTransferOwnershipResult: public Object { public: }; @@ -3958,14 +4002,15 @@ class chatBoostFeatures final : public Object { int32 min_chat_theme_background_boost_level_; int32 min_custom_background_boost_level_; int32 min_custom_emoji_sticker_set_boost_level_; + int32 min_automatic_translation_boost_level_; int32 min_speech_recognition_boost_level_; int32 min_sponsored_message_disable_boost_level_; chatBoostFeatures(); - chatBoostFeatures(array> &&features_, int32 min_profile_background_custom_emoji_boost_level_, int32 min_background_custom_emoji_boost_level_, int32 min_emoji_status_boost_level_, int32 min_chat_theme_background_boost_level_, int32 min_custom_background_boost_level_, int32 min_custom_emoji_sticker_set_boost_level_, int32 min_speech_recognition_boost_level_, int32 min_sponsored_message_disable_boost_level_); + chatBoostFeatures(array> &&features_, int32 min_profile_background_custom_emoji_boost_level_, int32 min_background_custom_emoji_boost_level_, int32 min_emoji_status_boost_level_, int32 min_chat_theme_background_boost_level_, int32 min_custom_background_boost_level_, int32 min_custom_emoji_sticker_set_boost_level_, int32 min_automatic_translation_boost_level_, int32 min_speech_recognition_boost_level_, int32 min_sponsored_message_disable_boost_level_); - static const std::int32_t ID = 866182642; + static const std::int32_t ID = -940531367; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -3988,14 +4033,15 @@ class chatBoostLevelFeatures final : public Object { int32 chat_theme_background_count_; bool can_set_custom_background_; bool can_set_custom_emoji_sticker_set_; + bool can_enable_automatic_translation_; bool can_recognize_speech_; bool can_disable_sponsored_messages_; chatBoostLevelFeatures(); - chatBoostLevelFeatures(int32 level_, int32 story_per_day_count_, int32 custom_emoji_reaction_count_, int32 title_color_count_, int32 profile_accent_color_count_, bool can_set_profile_background_custom_emoji_, int32 accent_color_count_, bool can_set_background_custom_emoji_, bool can_set_emoji_status_, int32 chat_theme_background_count_, bool can_set_custom_background_, bool can_set_custom_emoji_sticker_set_, bool can_recognize_speech_, bool can_disable_sponsored_messages_); + chatBoostLevelFeatures(int32 level_, int32 story_per_day_count_, int32 custom_emoji_reaction_count_, int32 title_color_count_, int32 profile_accent_color_count_, bool can_set_profile_background_custom_emoji_, int32 accent_color_count_, bool can_set_background_custom_emoji_, bool can_set_emoji_status_, int32 chat_theme_background_count_, bool can_set_custom_background_, bool can_set_custom_emoji_sticker_set_, bool can_enable_automatic_translation_, bool can_recognize_speech_, bool can_disable_sponsored_messages_); - static const std::int32_t ID = -189458156; + static const std::int32_t ID = 975439470; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -4852,6 +4898,23 @@ class chatEventShowMessageSenderToggled final : public ChatEventAction { void store(TlStorerToString &s, const char *field_name) const final; }; +class chatEventAutomaticTranslationToggled final : public ChatEventAction { + std::int32_t get_id() const final { + return ID; + } + + public: + bool has_automatic_translation_; + + chatEventAutomaticTranslationToggled(); + + explicit chatEventAutomaticTranslationToggled(bool has_automatic_translation_); + + static const std::int32_t ID = 194147926; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class chatEventInviteLinkEdited final : public ChatEventAction { std::int32_t get_id() const final { return ID; @@ -5968,8 +6031,8 @@ class chatNotificationSettings final : public Object { bool mute_stories_; bool use_default_story_sound_; int64 story_sound_id_; - bool use_default_show_story_sender_; - bool show_story_sender_; + bool use_default_show_story_poster_; + bool show_story_poster_; bool use_default_disable_pinned_message_notifications_; bool disable_pinned_message_notifications_; bool use_default_disable_mention_notifications_; @@ -5977,9 +6040,9 @@ class chatNotificationSettings final : public Object { chatNotificationSettings(); - chatNotificationSettings(bool use_default_mute_for_, int32 mute_for_, bool use_default_sound_, int64 sound_id_, bool use_default_show_preview_, bool show_preview_, bool use_default_mute_stories_, bool mute_stories_, bool use_default_story_sound_, int64 story_sound_id_, bool use_default_show_story_sender_, bool show_story_sender_, bool use_default_disable_pinned_message_notifications_, bool disable_pinned_message_notifications_, bool use_default_disable_mention_notifications_, bool disable_mention_notifications_); + chatNotificationSettings(bool use_default_mute_for_, int32 mute_for_, bool use_default_sound_, int64 sound_id_, bool use_default_show_preview_, bool show_preview_, bool use_default_mute_stories_, bool mute_stories_, bool use_default_story_sound_, int64 story_sound_id_, bool use_default_show_story_poster_, bool show_story_poster_, bool use_default_disable_pinned_message_notifications_, bool disable_pinned_message_notifications_, bool use_default_disable_mention_notifications_, bool disable_mention_notifications_); - static const std::int32_t ID = -572779825; + static const std::int32_t ID = 1459533846; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -7206,6 +7269,23 @@ class customRequestResult final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class data final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + bytes data_; + + data(); + + explicit data(bytes const &data_); + + static const std::int32_t ID = 221197337; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class databaseStatistics final : public Object { std::int32_t get_id() const final { return ID; @@ -8250,23 +8330,6 @@ class fileDownloadedPrefixSize final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; -class filePart final : public Object { - std::int32_t get_id() const final { - return ID; - } - - public: - bytes data_; - - filePart(); - - explicit filePart(bytes const &data_); - - static const std::int32_t ID = 911821878; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - class FileType: public Object { public: }; @@ -9135,6 +9198,72 @@ class gift final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class upgradedGift; + +class giftForResale final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr gift_; + string received_gift_id_; + + giftForResale(); + + giftForResale(object_ptr &&gift_, string const &received_gift_id_); + + static const std::int32_t ID = 1130990515; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class GiftForResaleOrder: public Object { + public: +}; + +class giftForResaleOrderPrice final : public GiftForResaleOrder { + std::int32_t get_id() const final { + return ID; + } + + public: + + giftForResaleOrderPrice(); + + static const std::int32_t ID = 1371740258; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class giftForResaleOrderPriceChangeDate final : public GiftForResaleOrder { + std::int32_t get_id() const final { + return ID; + } + + public: + + giftForResaleOrderPriceChangeDate(); + + static const std::int32_t ID = -1694144054; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class giftForResaleOrderNumber final : public GiftForResaleOrder { + std::int32_t get_id() const final { + return ID; + } + + public: + + giftForResaleOrderNumber(); + + static const std::int32_t ID = -1301157632; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class acceptedGiftTypes; class giftSettings final : public Object { @@ -9180,21 +9309,32 @@ class giftUpgradePreview final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; -class gift; +class giftForResale; -class gifts final : public Object { +class upgradedGiftBackdropCount; + +class upgradedGiftModelCount; + +class upgradedGiftSymbolCount; + +class giftsForResale final : public Object { std::int32_t get_id() const final { return ID; } public: - array> gifts_; + int32 total_count_; + array> gifts_; + array> models_; + array> symbols_; + array> backdrops_; + string next_offset_; - gifts(); + giftsForResale(); - explicit gifts(array> &&gifts_); + giftsForResale(int32 total_count_, array> &&gifts_, array> &&models_, array> &&symbols_, array> &&backdrops_, string const &next_offset_); - static const std::int32_t ID = 1652323382; + static const std::int32_t ID = 35082425; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -9401,14 +9541,16 @@ class groupCall final : public Object { public: int32 id_; - int32 from_call_id_; string title_; + string invite_link_; int32 scheduled_start_date_; bool enabled_start_notification_; bool is_active_; + bool is_video_chat_; bool is_rtmp_stream_; bool is_joined_; bool need_rejoin_; + bool is_owned_; bool can_be_managed_; int32 participant_count_; bool has_hidden_listeners_; @@ -9425,9 +9567,41 @@ class groupCall final : public Object { groupCall(); - groupCall(int32 id_, int32 from_call_id_, string const &title_, int32 scheduled_start_date_, bool enabled_start_notification_, bool is_active_, bool is_rtmp_stream_, bool is_joined_, bool need_rejoin_, bool can_be_managed_, int32 participant_count_, bool has_hidden_listeners_, bool loaded_all_participants_, array> &&recent_speakers_, bool is_my_video_enabled_, bool is_my_video_paused_, bool can_enable_video_, bool mute_new_participants_, bool can_toggle_mute_new_participants_, int32 record_duration_, bool is_video_recorded_, int32 duration_); + groupCall(int32 id_, string const &title_, string const &invite_link_, int32 scheduled_start_date_, bool enabled_start_notification_, bool is_active_, bool is_video_chat_, bool is_rtmp_stream_, bool is_joined_, bool need_rejoin_, bool is_owned_, bool can_be_managed_, int32 participant_count_, bool has_hidden_listeners_, bool loaded_all_participants_, array> &&recent_speakers_, bool is_my_video_enabled_, bool is_my_video_paused_, bool can_enable_video_, bool mute_new_participants_, bool can_toggle_mute_new_participants_, int32 record_duration_, bool is_video_recorded_, int32 duration_); - static const std::int32_t ID = 186241327; + static const std::int32_t ID = -144438687; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class GroupCallDataChannel: public Object { + public: +}; + +class groupCallDataChannelMain final : public GroupCallDataChannel { + std::int32_t get_id() const final { + return ID; + } + + public: + + groupCallDataChannelMain(); + + static const std::int32_t ID = -32177779; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class groupCallDataChannelScreenSharing final : public GroupCallDataChannel { + std::int32_t get_id() const final { + return ID; + } + + public: + + groupCallDataChannelScreenSharing(); + + static const std::int32_t ID = -601649103; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -9449,6 +9623,44 @@ class groupCallId final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class groupCallInfo final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + string join_payload_; + + groupCallInfo(); + + groupCallInfo(int32 group_call_id_, string const &join_payload_); + + static const std::int32_t ID = 892575956; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class groupCallJoinParameters final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 audio_source_id_; + string payload_; + bool is_muted_; + bool is_my_video_enabled_; + + groupCallJoinParameters(); + + groupCallJoinParameters(int32 audio_source_id_, string const &payload_, bool is_muted_, bool is_my_video_enabled_); + + static const std::int32_t ID = 1763438054; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class MessageSender; class groupCallParticipantVideoInfo; @@ -9510,6 +9722,26 @@ class groupCallParticipantVideoInfo final : public Object { class MessageSender; +class groupCallParticipants final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 total_count_; + array> participant_ids_; + + groupCallParticipants(); + + groupCallParticipants(int32 total_count_, array> &&participant_ids_); + + static const std::int32_t ID = -1042491570; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class MessageSender; + class groupCallRecentSpeaker final : public Object { std::int32_t get_id() const final { return ID; @@ -9528,44 +9760,6 @@ class groupCallRecentSpeaker final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; -class groupCallStream final : public Object { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 channel_id_; - int32 scale_; - int53 time_offset_; - - groupCallStream(); - - groupCallStream(int32 channel_id_, int32 scale_, int53 time_offset_); - - static const std::int32_t ID = -264564795; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class groupCallStream; - -class groupCallStreams final : public Object { - std::int32_t get_id() const final { - return ID; - } - - public: - array> streams_; - - groupCallStreams(); - - explicit groupCallStreams(array> &&streams_); - - static const std::int32_t ID = -1032959578; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - class GroupCallVideoQuality: public Object { public: }; @@ -10564,6 +10758,45 @@ class inputFileGenerated final : public InputFile { void store(TlStorerToString &s, const char *field_name) const final; }; +class InputGroupCall: public Object { + public: +}; + +class inputGroupCallLink final : public InputGroupCall { + std::int32_t get_id() const final { + return ID; + } + + public: + string link_; + + inputGroupCallLink(); + + explicit inputGroupCallLink(string const &link_); + + static const std::int32_t ID = -812157480; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class inputGroupCallMessage final : public InputGroupCall { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 chat_id_; + int53 message_id_; + + inputGroupCallMessage(); + + inputGroupCallMessage(int53 chat_id_, int53 message_id_); + + static const std::int32_t ID = -341793768; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class InputFile; class date; @@ -11350,14 +11583,14 @@ class inputMessageStory final : public InputMessageContent { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; inputMessageStory(); - inputMessageStory(int53 story_sender_chat_id_, int32 story_id_); + inputMessageStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 554278243; + static const std::int32_t ID = -370732053; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -11433,14 +11666,14 @@ class inputMessageReplyToStory final : public InputMessageReplyTo { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; inputMessageReplyToStory(); - inputMessageReplyToStory(int53 story_sender_chat_id_, int32 story_id_); + inputMessageReplyToStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 1370410616; + static const std::int32_t ID = -1723842320; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -12570,6 +12803,23 @@ class internalLinkTypeGame final : public InternalLinkType { void store(TlStorerToString &s, const char *field_name) const final; }; +class internalLinkTypeGroupCall final : public InternalLinkType { + std::int32_t get_id() const final { + return ID; + } + + public: + string invite_link_; + + internalLinkTypeGroupCall(); + + explicit internalLinkTypeGroupCall(string const &invite_link_); + + static const std::int32_t ID = 1953084438; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class internalLinkTypeInstantView final : public InternalLinkType { std::int32_t get_id() const final { return ID; @@ -12690,6 +12940,20 @@ class internalLinkTypeMessageDraft final : public InternalLinkType { void store(TlStorerToString &s, const char *field_name) const final; }; +class internalLinkTypeMyStars final : public InternalLinkType { + std::int32_t get_id() const final { + return ID; + } + + public: + + internalLinkTypeMyStars(); + + static const std::int32_t ID = 1613887070; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class internalLinkTypePassportDataRequest final : public InternalLinkType { std::int32_t get_id() const final { return ID; @@ -12898,14 +13162,14 @@ class internalLinkTypeStory final : public InternalLinkType { } public: - string story_sender_username_; + string story_poster_username_; int32 story_id_; internalLinkTypeStory(); - internalLinkTypeStory(string const &story_sender_username_, int32 story_id_); + internalLinkTypeStory(string const &story_poster_username_, int32 story_id_); - static const std::int32_t ID = 1471997511; + static const std::int32_t ID = 1852042869; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -13064,6 +13328,70 @@ class internalLinkTypeWebApp final : public InternalLinkType { void store(TlStorerToString &s, const char *field_name) const final; }; +class InviteGroupCallParticipantResult: public Object { + public: +}; + +class inviteGroupCallParticipantResultUserPrivacyRestricted final : public InviteGroupCallParticipantResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + inviteGroupCallParticipantResultUserPrivacyRestricted(); + + static const std::int32_t ID = 53003769; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class inviteGroupCallParticipantResultUserAlreadyParticipant final : public InviteGroupCallParticipantResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + inviteGroupCallParticipantResultUserAlreadyParticipant(); + + static const std::int32_t ID = 661526151; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class inviteGroupCallParticipantResultUserWasBanned final : public InviteGroupCallParticipantResult { + std::int32_t get_id() const final { + return ID; + } + + public: + + inviteGroupCallParticipantResultUserWasBanned(); + + static const std::int32_t ID = -204345357; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class inviteGroupCallParticipantResultSuccess final : public InviteGroupCallParticipantResult { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 chat_id_; + int53 message_id_; + + inviteGroupCallParticipantResultSuccess(); + + inviteGroupCallParticipantResultSuccess(int53 chat_id_, int53 message_id_); + + static const std::int32_t ID = -1914309427; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class InviteLinkChatType: public Object { public: }; @@ -13958,6 +14286,20 @@ class linkPreviewTypeExternalVideo final : public LinkPreviewType { void store(TlStorerToString &s, const char *field_name) const final; }; +class linkPreviewTypeGroupCall final : public LinkPreviewType { + std::int32_t get_id() const final { + return ID; + } + + public: + + linkPreviewTypeGroupCall(); + + static const std::int32_t ID = -1242459936; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class linkPreviewTypeInvoice final : public LinkPreviewType { std::int32_t get_id() const final { return ID; @@ -14071,14 +14413,14 @@ class linkPreviewTypeStory final : public LinkPreviewType { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; linkPreviewTypeStory(); - linkPreviewTypeStory(int53 story_sender_chat_id_, int32 story_id_); + linkPreviewTypeStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 513574862; + static const std::int32_t ID = 1045709531; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -15165,15 +15507,15 @@ class messageStory final : public MessageContent { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; bool via_mention_; messageStory(); - messageStory(int53 story_sender_chat_id_, int32 story_id_, bool via_mention_); + messageStory(int53 story_poster_chat_id_, int32 story_id_, bool via_mention_); - static const std::int32_t ID = 858387156; + static const std::int32_t ID = 1514236353; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -15222,6 +15564,27 @@ class messageCall final : public MessageContent { void store(TlStorerToString &s, const char *field_name) const final; }; +class messageGroupCall final : public MessageContent { + std::int32_t get_id() const final { + return ID; + } + + public: + bool is_active_; + bool was_missed_; + bool is_video_; + int32 duration_; + array> other_participant_ids_; + + messageGroupCall(); + + messageGroupCall(bool is_active_, bool was_missed_, bool is_video_, int32 duration_, array> &&other_participant_ids_); + + static const std::int32_t ID = 1370396295; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class messageVideoChatScheduled final : public MessageContent { std::int32_t get_id() const final { return ID; @@ -15995,14 +16358,17 @@ class messageUpgradedGift final : public MessageContent { bool is_saved_; bool can_be_transferred_; bool was_transferred_; + int53 last_resale_star_count_; int53 transfer_star_count_; + int32 next_transfer_date_; + int32 next_resale_date_; int32 export_date_; messageUpgradedGift(); - messageUpgradedGift(object_ptr &&gift_, object_ptr &&sender_id_, string const &received_gift_id_, bool is_upgrade_, bool is_saved_, bool can_be_transferred_, bool was_transferred_, int53 transfer_star_count_, int32 export_date_); + messageUpgradedGift(object_ptr &&gift_, object_ptr &&sender_id_, string const &received_gift_id_, bool is_upgrade_, bool is_saved_, bool can_be_transferred_, bool was_transferred_, int53 last_resale_star_count_, int53 transfer_star_count_, int32 next_transfer_date_, int32 next_resale_date_, int32 export_date_); - static const std::int32_t ID = -1068647053; + static const std::int32_t ID = -1586797067; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -16822,14 +17188,14 @@ class messageReplyToStory final : public MessageReplyTo { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; messageReplyToStory(); - messageReplyToStory(int53 story_sender_chat_id_, int32 story_id_); + messageReplyToStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 1888266553; + static const std::int32_t ID = -674492596; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -20798,30 +21164,30 @@ class premiumLimitTypeActiveStoryCount final : public PremiumLimitType { void store(TlStorerToString &s, const char *field_name) const final; }; -class premiumLimitTypeWeeklySentStoryCount final : public PremiumLimitType { +class premiumLimitTypeWeeklyPostedStoryCount final : public PremiumLimitType { std::int32_t get_id() const final { return ID; } public: - premiumLimitTypeWeeklySentStoryCount(); + premiumLimitTypeWeeklyPostedStoryCount(); - static const std::int32_t ID = 40485707; + static const std::int32_t ID = -506354313; void store(TlStorerToString &s, const char *field_name) const final; }; -class premiumLimitTypeMonthlySentStoryCount final : public PremiumLimitType { +class premiumLimitTypeMonthlyPostedStoryCount final : public PremiumLimitType { std::int32_t get_id() const final { return ID; } public: - premiumLimitTypeMonthlySentStoryCount(); + premiumLimitTypeMonthlyPostedStoryCount(); - static const std::int32_t ID = 819481475; + static const std::int32_t ID = 26329490; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -22516,13 +22882,15 @@ class receivedGift final : public Object { int53 sell_star_count_; int53 prepaid_upgrade_star_count_; int53 transfer_star_count_; + int32 next_transfer_date_; + int32 next_resale_date_; int32 export_date_; receivedGift(); - receivedGift(string const &received_gift_id_, object_ptr &&sender_id_, object_ptr &&text_, bool is_private_, bool is_saved_, bool is_pinned_, bool can_be_upgraded_, bool can_be_transferred_, bool was_refunded_, int32 date_, object_ptr &&gift_, int53 sell_star_count_, int53 prepaid_upgrade_star_count_, int53 transfer_star_count_, int32 export_date_); + receivedGift(string const &received_gift_id_, object_ptr &&sender_id_, object_ptr &&text_, bool is_private_, bool is_saved_, bool is_pinned_, bool can_be_upgraded_, bool can_be_transferred_, bool was_refunded_, int32 date_, object_ptr &&gift_, int53 sell_star_count_, int53 prepaid_upgrade_star_count_, int53 transfer_star_count_, int32 next_transfer_date_, int32 next_resale_date_, int32 export_date_); - static const std::int32_t ID = 553476056; + static const std::int32_t ID = 61550296; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -23704,15 +24072,15 @@ class scopeNotificationSettings final : public Object { bool use_default_mute_stories_; bool mute_stories_; int64 story_sound_id_; - bool show_story_sender_; + bool show_story_poster_; bool disable_pinned_message_notifications_; bool disable_mention_notifications_; scopeNotificationSettings(); - scopeNotificationSettings(int32 mute_for_, int64 sound_id_, bool show_preview_, bool use_default_mute_stories_, bool mute_stories_, int64 story_sound_id_, bool show_story_sender_, bool disable_pinned_message_notifications_, bool disable_mention_notifications_); + scopeNotificationSettings(int32 mute_for_, int64 sound_id_, bool show_preview_, bool use_default_mute_stories_, bool mute_stories_, int64 story_sound_id_, bool show_story_poster_, bool disable_pinned_message_notifications_, bool disable_mention_notifications_); - static const std::int32_t ID = -599105185; + static const std::int32_t ID = 88369150; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -25440,6 +25808,43 @@ class starTransactionTypeGiftUpgrade final : public StarTransactionType { void store(TlStorerToString &s, const char *field_name) const final; }; +class starTransactionTypeUpgradedGiftPurchase final : public StarTransactionType { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 user_id_; + object_ptr gift_; + + starTransactionTypeUpgradedGiftPurchase(); + + starTransactionTypeUpgradedGiftPurchase(int53 user_id_, object_ptr &&gift_); + + static const std::int32_t ID = -59050247; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class starTransactionTypeUpgradedGiftSale final : public StarTransactionType { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 user_id_; + object_ptr gift_; + object_ptr affiliate_; + + starTransactionTypeUpgradedGiftSale(); + + starTransactionTypeUpgradedGiftSale(int53 user_id_, object_ptr &&gift_, object_ptr &&affiliate_); + + static const std::int32_t ID = 2041948603; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class starTransactionTypeChannelPaidReactionSend final : public StarTransactionType { std::int32_t get_id() const final { return ID; @@ -26316,10 +26721,10 @@ class story final : public Object { public: int32 id_; - int53 sender_chat_id_; - object_ptr sender_id_; + int53 poster_chat_id_; + object_ptr poster_id_; int32 date_; - bool is_being_sent_; + bool is_being_posted_; bool is_being_edited_; bool is_edited_; bool is_posted_to_chat_page_; @@ -26342,9 +26747,9 @@ class story final : public Object { story(); - story(int32 id_, int53 sender_chat_id_, object_ptr &&sender_id_, int32 date_, bool is_being_sent_, bool is_being_edited_, bool is_edited_, bool is_posted_to_chat_page_, bool is_visible_only_for_self_, bool can_be_deleted_, bool can_be_edited_, bool can_be_forwarded_, bool can_be_replied_, bool can_toggle_is_posted_to_chat_page_, bool can_get_statistics_, bool can_get_interactions_, bool has_expired_viewers_, object_ptr &&repost_info_, object_ptr &&interaction_info_, object_ptr &&chosen_reaction_type_, object_ptr &&privacy_settings_, object_ptr &&content_, array> &&areas_, object_ptr &&caption_); + story(int32 id_, int53 poster_chat_id_, object_ptr &&poster_id_, int32 date_, bool is_being_posted_, bool is_being_edited_, bool is_edited_, bool is_posted_to_chat_page_, bool is_visible_only_for_self_, bool can_be_deleted_, bool can_be_edited_, bool can_be_forwarded_, bool can_be_replied_, bool can_toggle_is_posted_to_chat_page_, bool can_get_statistics_, bool can_get_interactions_, bool has_expired_viewers_, object_ptr &&repost_info_, object_ptr &&interaction_info_, object_ptr &&chosen_reaction_type_, object_ptr &&privacy_settings_, object_ptr &&content_, array> &&areas_, object_ptr &&caption_); - static const std::int32_t ID = -294015331; + static const std::int32_t ID = 1206531707; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -26594,14 +26999,14 @@ class storyFullId final : public Object { } public: - int53 sender_chat_id_; + int53 poster_chat_id_; int32 story_id_; storyFullId(); - storyFullId(int53 sender_chat_id_, int32 story_id_); + storyFullId(int53 poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 1880961525; + static const std::int32_t ID = 765952419; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -26815,13 +27220,13 @@ class storyOriginHiddenUser final : public StoryOrigin { } public: - string sender_name_; + string poster_name_; storyOriginHiddenUser(); - explicit storyOriginHiddenUser(string const &sender_name_); + explicit storyOriginHiddenUser(string const &poster_name_); - static const std::int32_t ID = 1512016364; + static const std::int32_t ID = -96348585; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -26967,6 +27372,8 @@ class storyVideo final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class formattedText; + class SuggestedAction: public Object { public: }; @@ -27176,6 +27583,26 @@ class suggestedActionExtendStarSubscriptions final : public SuggestedAction { void store(TlStorerToString &s, const char *field_name) const final; }; +class suggestedActionCustom final : public SuggestedAction { + std::int32_t get_id() const final { + return ID; + } + + public: + string name_; + object_ptr title_; + object_ptr description_; + string url_; + + suggestedActionCustom(); + + suggestedActionCustom(string const &name_, object_ptr &&title_, object_ptr &&description_, string const &url_); + + static const std::int32_t ID = 2092876611; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class ChatMemberStatus; class usernames; @@ -27194,6 +27621,7 @@ class supergroup final : public Object { object_ptr status_; int32 member_count_; int32 boost_level_; + bool has_automatic_translation_; bool has_linked_chat_; bool has_location_; bool sign_messages_; @@ -27213,9 +27641,9 @@ class supergroup final : public Object { supergroup(); - supergroup(int53 id_, object_ptr &&usernames_, int32 date_, object_ptr &&status_, int32 member_count_, int32 boost_level_, bool has_linked_chat_, bool has_location_, bool sign_messages_, bool show_message_sender_, bool join_to_send_messages_, bool join_by_request_, bool is_slow_mode_enabled_, bool is_channel_, bool is_broadcast_group_, bool is_forum_, object_ptr &&verification_status_, bool has_sensitive_content_, string const &restriction_reason_, int53 paid_message_star_count_, bool has_active_stories_, bool has_unread_active_stories_); + supergroup(int53 id_, object_ptr &&usernames_, int32 date_, object_ptr &&status_, int32 member_count_, int32 boost_level_, bool has_automatic_translation_, bool has_linked_chat_, bool has_location_, bool sign_messages_, bool show_message_sender_, bool join_to_send_messages_, bool join_by_request_, bool is_slow_mode_enabled_, bool is_channel_, bool is_broadcast_group_, bool is_forum_, object_ptr &&verification_status_, bool has_sensitive_content_, string const &restriction_reason_, int53 paid_message_star_count_, bool has_active_stories_, bool has_unread_active_stories_); - static const std::int32_t ID = 992231696; + static const std::int32_t ID = -1355919719; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -28763,7 +29191,7 @@ class BlockList; class CallbackQueryPayload; -class CanSendStoryResult; +class CanPostStoryResult; class ChatAction; @@ -29988,14 +30416,15 @@ class updateForumTopic final : public Update { int53 chat_id_; int53 message_thread_id_; bool is_pinned_; + int53 last_read_inbox_message_id_; int53 last_read_outbox_message_id_; object_ptr notification_settings_; updateForumTopic(); - updateForumTopic(int53 chat_id_, int53 message_thread_id_, bool is_pinned_, int53 last_read_outbox_message_id_, object_ptr &¬ification_settings_); + updateForumTopic(int53 chat_id_, int53 message_thread_id_, bool is_pinned_, int53 last_read_inbox_message_id_, int53 last_read_outbox_message_id_, object_ptr &¬ification_settings_); - static const std::int32_t ID = 807069971; + static const std::int32_t ID = 266794534; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -30529,6 +30958,43 @@ class updateGroupCallParticipant final : public Update { void store(TlStorerToString &s, const char *field_name) const final; }; +class updateGroupCallParticipants final : public Update { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + array participant_user_ids_; + + updateGroupCallParticipants(); + + updateGroupCallParticipants(int32 group_call_id_, array &&participant_user_ids_); + + static const std::int32_t ID = -1874006374; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class updateGroupCallVerificationState final : public Update { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + int32 generation_; + array emojis_; + + updateGroupCallVerificationState(); + + updateGroupCallVerificationState(int32 group_call_id_, int32 generation_, array &&emojis_); + + static const std::int32_t ID = 682965060; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class updateNewCallSignalingData final : public Update { std::int32_t get_id() const final { return ID; @@ -30629,19 +31095,19 @@ class updateStoryDeleted final : public Update { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; updateStoryDeleted(); - updateStoryDeleted(int53 story_sender_chat_id_, int32 story_id_); + updateStoryDeleted(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 1879567261; + static const std::int32_t ID = -582743166; void store(TlStorerToString &s, const char *field_name) const final; }; -class updateStorySendSucceeded final : public Update { +class updateStoryPostSucceeded final : public Update { std::int32_t get_id() const final { return ID; } @@ -30650,16 +31116,16 @@ class updateStorySendSucceeded final : public Update { object_ptr story_; int32 old_story_id_; - updateStorySendSucceeded(); + updateStoryPostSucceeded(); - updateStorySendSucceeded(object_ptr &&story_, int32 old_story_id_); + updateStoryPostSucceeded(object_ptr &&story_, int32 old_story_id_); - static const std::int32_t ID = -1188651433; + static const std::int32_t ID = -1712432318; void store(TlStorerToString &s, const char *field_name) const final; }; -class updateStorySendFailed final : public Update { +class updateStoryPostFailed final : public Update { std::int32_t get_id() const final { return ID; } @@ -30667,13 +31133,13 @@ class updateStorySendFailed final : public Update { public: object_ptr story_; object_ptr error_; - object_ptr error_type_; + object_ptr error_type_; - updateStorySendFailed(); + updateStoryPostFailed(); - updateStorySendFailed(object_ptr &&story_, object_ptr &&error_, object_ptr &&error_type_); + updateStoryPostFailed(object_ptr &&story_, object_ptr &&error_, object_ptr &&error_type_); - static const std::int32_t ID = -532221543; + static const std::int32_t ID = 1248168444; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -31817,13 +32283,15 @@ class upgradeGiftResult final : public Object { bool is_saved_; bool can_be_transferred_; int53 transfer_star_count_; + int32 next_transfer_date_; + int32 next_resale_date_; int32 export_date_; upgradeGiftResult(); - upgradeGiftResult(object_ptr &&gift_, string const &received_gift_id_, bool is_saved_, bool can_be_transferred_, int53 transfer_star_count_, int32 export_date_); + upgradeGiftResult(object_ptr &&gift_, string const &received_gift_id_, bool is_saved_, bool can_be_transferred_, int53 transfer_star_count_, int32 next_transfer_date_, int32 next_resale_date_, int32 export_date_); - static const std::int32_t ID = 664437354; + static const std::int32_t ID = 233376346; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -31858,12 +32326,68 @@ class upgradedGift final : public Object { object_ptr symbol_; object_ptr backdrop_; object_ptr original_details_; + int53 resale_star_count_; upgradedGift(); - upgradedGift(int64 id_, string const &title_, string const &name_, int32 number_, int32 total_upgraded_count_, int32 max_upgraded_count_, object_ptr &&owner_id_, string const &owner_address_, string const &owner_name_, string const &gift_address_, object_ptr &&model_, object_ptr &&symbol_, object_ptr &&backdrop_, object_ptr &&original_details_); + upgradedGift(int64 id_, string const &title_, string const &name_, int32 number_, int32 total_upgraded_count_, int32 max_upgraded_count_, object_ptr &&owner_id_, string const &owner_address_, string const &owner_name_, string const &gift_address_, object_ptr &&model_, object_ptr &&symbol_, object_ptr &&backdrop_, object_ptr &&original_details_, int53 resale_star_count_); - static const std::int32_t ID = -473646818; + static const std::int32_t ID = 992832410; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class UpgradedGiftAttributeId: public Object { + public: +}; + +class upgradedGiftAttributeIdModel final : public UpgradedGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int64 sticker_id_; + + upgradedGiftAttributeIdModel(); + + explicit upgradedGiftAttributeIdModel(int64 sticker_id_); + + static const std::int32_t ID = 1053287307; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class upgradedGiftAttributeIdSymbol final : public UpgradedGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int64 sticker_id_; + + upgradedGiftAttributeIdSymbol(); + + explicit upgradedGiftAttributeIdSymbol(int64 sticker_id_); + + static const std::int32_t ID = 1188205608; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class upgradedGiftAttributeIdBackdrop final : public UpgradedGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 backdrop_id_; + + upgradedGiftAttributeIdBackdrop(); + + explicit upgradedGiftAttributeIdBackdrop(int32 backdrop_id_); + + static const std::int32_t ID = 1461997935; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -31876,15 +32400,16 @@ class upgradedGiftBackdrop final : public Object { } public: + int32 id_; string name_; object_ptr colors_; int32 rarity_per_mille_; upgradedGiftBackdrop(); - upgradedGiftBackdrop(string const &name_, object_ptr &&colors_, int32 rarity_per_mille_); + upgradedGiftBackdrop(int32 id_, string const &name_, object_ptr &&colors_, int32 rarity_per_mille_); - static const std::int32_t ID = -334899886; + static const std::int32_t ID = -378289242; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -31909,6 +32434,26 @@ class upgradedGiftBackdropColors final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class upgradedGiftBackdrop; + +class upgradedGiftBackdropCount final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr backdrop_; + int32 total_count_; + + upgradedGiftBackdropCount(); + + upgradedGiftBackdropCount(object_ptr &&backdrop_, int32 total_count_); + + static const std::int32_t ID = -562274120; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class sticker; class upgradedGiftModel final : public Object { @@ -31930,6 +32475,26 @@ class upgradedGiftModel final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class upgradedGiftModel; + +class upgradedGiftModelCount final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr model_; + int32 total_count_; + + upgradedGiftModelCount(); + + upgradedGiftModelCount(object_ptr &&model_, int32 total_count_); + + static const std::int32_t ID = -1127238023; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class MessageSender; class formattedText; @@ -31975,6 +32540,26 @@ class upgradedGiftSymbol final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class upgradedGiftSymbol; + +class upgradedGiftSymbolCount final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr symbol_; + int32 total_count_; + + upgradedGiftSymbolCount(); + + upgradedGiftSymbolCount(object_ptr &&symbol_, int32 total_count_); + + static const std::int32_t ID = -773579874; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class UserStatus; class UserType; @@ -32871,6 +33456,44 @@ class videoChat final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class videoChatStream final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 channel_id_; + int32 scale_; + int53 time_offset_; + + videoChatStream(); + + videoChatStream(int32 channel_id_, int32 scale_, int53 time_offset_); + + static const std::int32_t ID = -1151707172; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class videoChatStream; + +class videoChatStreams final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + array> streams_; + + videoChatStreams(); + + explicit videoChatStreams(array> &&streams_); + + static const std::int32_t ID = -434622720; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class SpeechRecognitionResult; class file; @@ -33948,6 +34571,28 @@ class banChatMember final : public Function { class ok; +class banGroupCallParticipants final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + array user_ids_; + + banGroupCallParticipants(); + + banGroupCallParticipants(int32 group_call_id_, array &&user_ids_); + + static const std::int32_t ID = 624883173; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + class blockMessageSenderFromReplies final : public Function { std::int32_t get_id() const final { return ID; @@ -34013,6 +34658,27 @@ class canBotSendMessages final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class CanPostStoryResult; + +class canPostStory final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 chat_id_; + + canPostStory(); + + explicit canPostStory(int53 chat_id_); + + static const std::int32_t ID = 668621518; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class StorePaymentPurpose; class ok; @@ -34058,27 +34724,6 @@ class canSendMessageToUser final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class CanSendStoryResult; - -class canSendStory final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int53 chat_id_; - - canSendStory(); - - explicit canSendStory(int53 chat_id_); - - static const std::int32_t ID = -1226825365; - - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - class CanTransferOwnershipResult; class canTransferOwnership final : public Function { @@ -34937,14 +35582,14 @@ class closeStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; closeStory(); - closeStory(int53 story_sender_chat_id_, int32 story_id_); + closeStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = 1144852309; + static const std::int32_t ID = 1553967851; using ReturnType = object_ptr; @@ -35118,13 +35763,12 @@ class createCall final : public Function { int53 user_id_; object_ptr protocol_; bool is_video_; - int32 group_call_id_; createCall(); - createCall(int53 user_id_, object_ptr &&protocol_, bool is_video_, int32 group_call_id_); + createCall(int53 user_id_, object_ptr &&protocol_, bool is_video_); - static const std::int32_t ID = -1270805351; + static const std::int32_t ID = -1104663024; using ReturnType = object_ptr; @@ -35252,7 +35896,9 @@ class createForumTopic final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class ok; +class groupCallInfo; + +class groupCallJoinParameters; class createGroupCall final : public Function { std::int32_t get_id() const final { @@ -35260,15 +35906,15 @@ class createGroupCall final : public Function { } public: - int32 call_id_; + object_ptr join_parameters_; createGroupCall(); - explicit createGroupCall(int32 call_id_); + explicit createGroupCall(object_ptr &&join_parameters_); - static const std::int32_t ID = -2017962501; + static const std::int32_t ID = 1930068672; - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -35514,6 +36160,56 @@ class createVideoChat final : public Function { class ok; +class declineGroupCallInvitation final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 chat_id_; + int53 message_id_; + + declineGroupCallInvitation(); + + declineGroupCallInvitation(int53 chat_id_, int53 message_id_); + + static const std::int32_t ID = 1843919377; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class GroupCallDataChannel; + +class MessageSender; + +class data; + +class decryptGroupCallData final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + object_ptr participant_id_; + object_ptr data_channel_; + bytes data_; + + decryptGroupCallData(); + + decryptGroupCallData(int32 group_call_id_, object_ptr &&participant_id_, object_ptr &&data_channel_, bytes const &data_); + + static const std::int32_t ID = 1781743076; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + class deleteAccount final : public Function { std::int32_t get_id() const final { return ID; @@ -36216,14 +36912,14 @@ class deleteStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; deleteStory(); - deleteStory(int53 story_sender_chat_id_, int32 story_id_); + deleteStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = -1623871722; + static const std::int32_t ID = -2020144472; using ReturnType = object_ptr; @@ -36297,15 +36993,16 @@ class discardCall final : public Function { public: int32 call_id_; bool is_disconnected_; + string invite_link_; int32 duration_; bool is_video_; int64 connection_id_; discardCall(); - discardCall(int32 call_id_, bool is_disconnected_, int32 duration_, bool is_video_, int64 connection_id_); + discardCall(int32 call_id_, bool is_disconnected_, string const &invite_link_, int32 duration_, bool is_video_, int64 connection_id_); - static const std::int32_t ID = -1784044162; + static const std::int32_t ID = -1545983346; using ReturnType = object_ptr; @@ -36612,7 +37309,7 @@ class editBusinessStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; object_ptr content_; object_ptr areas_; @@ -36621,9 +37318,9 @@ class editBusinessStory final : public Function { editBusinessStory(); - editBusinessStory(int53 story_sender_chat_id_, int32 story_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_, object_ptr &&privacy_settings_); + editBusinessStory(int53 story_poster_chat_id_, int32 story_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_, object_ptr &&privacy_settings_); - static const std::int32_t ID = -805339579; + static const std::int32_t ID = 472538940; using ReturnType = object_ptr; @@ -37165,7 +37862,7 @@ class editStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; object_ptr content_; object_ptr areas_; @@ -37173,9 +37870,9 @@ class editStory final : public Function { editStory(); - editStory(int53 story_sender_chat_id_, int32 story_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_); + editStory(int53 story_poster_chat_id_, int32 story_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_); - static const std::int32_t ID = 1584013745; + static const std::int32_t ID = 355296788; using ReturnType = object_ptr; @@ -37190,15 +37887,15 @@ class editStoryCover final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; double cover_frame_timestamp_; editStoryCover(); - editStoryCover(int53 story_sender_chat_id_, int32 story_id_, double cover_frame_timestamp_); + editStoryCover(int53 story_poster_chat_id_, int32 story_id_, double cover_frame_timestamp_); - static const std::int32_t ID = -1423307701; + static const std::int32_t ID = -1035823266; using ReturnType = object_ptr; @@ -37249,6 +37946,32 @@ class enableProxy final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class GroupCallDataChannel; + +class data; + +class encryptGroupCallData final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + object_ptr data_channel_; + bytes data_; + int32 unencrypted_prefix_size_; + + encryptGroupCallData(); + + encryptGroupCallData(int32 group_call_id_, object_ptr &&data_channel_, bytes const &data_, int32 unencrypted_prefix_size_); + + static const std::int32_t ID = -377997690; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class ok; class endGroupCall final : public Function { @@ -37662,7 +38385,7 @@ class getAvailableChatBoostSlots final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class gifts; +class availableGifts; class getAvailableGifts final : public Function { std::int32_t get_id() const final { @@ -37673,9 +38396,9 @@ class getAvailableGifts final : public Function { getAvailableGifts(); - static const std::int32_t ID = -153786901; + static const std::int32_t ID = -4559695; - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -39103,7 +39826,7 @@ class getChatStoryInteractions final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; object_ptr reaction_type_; bool prefer_forwards_; @@ -39112,9 +39835,9 @@ class getChatStoryInteractions final : public Function { getChatStoryInteractions(); - getChatStoryInteractions(int53 story_sender_chat_id_, int32 story_id_, object_ptr &&reaction_type_, bool prefer_forwards_, string const &offset_, int32 limit_); + getChatStoryInteractions(int53 story_poster_chat_id_, int32 story_id_, object_ptr &&reaction_type_, bool prefer_forwards_, string const &offset_, int32 limit_); - static const std::int32_t ID = -974359690; + static const std::int32_t ID = 354545268; using ReturnType = object_ptr; @@ -39168,16 +39891,16 @@ class getChatsForChatFolderInviteLink final : public Function { class chats; -class getChatsToSendStories final : public Function { +class getChatsToPostStories final : public Function { std::int32_t get_id() const final { return ID; } public: - getChatsToSendStories(); + getChatsToPostStories(); - static const std::int32_t ID = 586802084; + static const std::int32_t ID = 1893901427; using ReturnType = object_ptr; @@ -40085,72 +40808,26 @@ class getGroupCall final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class httpUrl; +class InputGroupCall; -class getGroupCallInviteLink final : public Function { +class groupCallParticipants; + +class getGroupCallParticipants final : public Function { std::int32_t get_id() const final { return ID; } public: - int32 group_call_id_; - bool can_self_unmute_; + object_ptr input_group_call_; + int32 limit_; - getGroupCallInviteLink(); + getGroupCallParticipants(); - getGroupCallInviteLink(int32 group_call_id_, bool can_self_unmute_); + getGroupCallParticipants(object_ptr &&input_group_call_, int32 limit_); - static const std::int32_t ID = 719407396; + static const std::int32_t ID = 1986739394; - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class GroupCallVideoQuality; - -class filePart; - -class getGroupCallStreamSegment final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 group_call_id_; - int53 time_offset_; - int32 scale_; - int32 channel_id_; - object_ptr video_quality_; - - getGroupCallStreamSegment(); - - getGroupCallStreamSegment(int32 group_call_id_, int53 time_offset_, int32 scale_, int32 channel_id_, object_ptr &&video_quality_); - - static const std::int32_t ID = -2077959515; - - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class groupCallStreams; - -class getGroupCallStreams final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 group_call_id_; - - getGroupCallStreams(); - - explicit getGroupCallStreams(int32 group_call_id_); - - static const std::int32_t ID = -1619226268; - - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -42633,15 +43310,15 @@ class getStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; bool only_local_; getStory(); - getStory(int53 story_sender_chat_id_, int32 story_id_, bool only_local_); + getStory(int53 story_poster_chat_id_, int32 story_id_, bool only_local_); - static const std::int32_t ID = 1903893624; + static const std::int32_t ID = -2011076366; using ReturnType = object_ptr; @@ -42722,16 +43399,16 @@ class getStoryPublicForwards final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; string offset_; int32 limit_; getStoryPublicForwards(); - getStoryPublicForwards(int53 story_sender_chat_id_, int32 story_id_, string const &offset_, int32 limit_); + getStoryPublicForwards(int53 story_poster_chat_id_, int32 story_id_, string const &offset_, int32 limit_); - static const std::int32_t ID = 1761074363; + static const std::int32_t ID = 1810378546; using ReturnType = object_ptr; @@ -43340,6 +44017,28 @@ class getVideoChatAvailableParticipants final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class httpUrl; + +class getVideoChatInviteLink final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + bool can_self_unmute_; + + getVideoChatInviteLink(); + + getVideoChatInviteLink(int32 group_call_id_, bool can_self_unmute_); + + static const std::int32_t ID = -1394707321; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class rtmpUrl; class getVideoChatRtmpUrl final : public Function { @@ -43361,6 +44060,54 @@ class getVideoChatRtmpUrl final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class GroupCallVideoQuality; + +class data; + +class getVideoChatStreamSegment final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + int53 time_offset_; + int32 scale_; + int32 channel_id_; + object_ptr video_quality_; + + getVideoChatStreamSegment(); + + getVideoChatStreamSegment(int32 group_call_id_, int53 time_offset_, int32 scale_, int32 channel_id_, object_ptr &&video_quality_); + + static const std::int32_t ID = 773343220; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class videoChatStreams; + +class getVideoChatStreams final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + + getVideoChatStreams(); + + explicit getVideoChatStreams(int32 group_call_id_); + + static const std::int32_t ID = 531757765; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class httpUrl; class webAppOpenParameters; @@ -43572,9 +44319,32 @@ class importMessages final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class InviteGroupCallParticipantResult; + +class inviteGroupCallParticipant final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + int53 user_id_; + bool is_video_; + + inviteGroupCallParticipant(); + + inviteGroupCallParticipant(int32 group_call_id_, int53 user_id_, bool is_video_); + + static const std::int32_t ID = -631535414; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class ok; -class inviteGroupCallParticipants final : public Function { +class inviteVideoChatParticipants final : public Function { std::int32_t get_id() const final { return ID; } @@ -43583,11 +44353,11 @@ class inviteGroupCallParticipants final : public Function { int32 group_call_id_; array user_ids_; - inviteGroupCallParticipants(); + inviteVideoChatParticipants(); - inviteGroupCallParticipants(int32 group_call_id_, array &&user_ids_); + inviteVideoChatParticipants(int32 group_call_id_, array &&user_ids_); - static const std::int32_t ID = 1867097679; + static const std::int32_t ID = -473149298; using ReturnType = object_ptr; @@ -43636,9 +44406,11 @@ class joinChatByInviteLink final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class MessageSender; +class InputGroupCall; -class text; +class groupCallInfo; + +class groupCallJoinParameters; class joinGroupCall final : public Function { std::int32_t get_id() const final { @@ -43646,20 +44418,42 @@ class joinGroupCall final : public Function { } public: - int32 group_call_id_; - object_ptr participant_id_; - int32 audio_source_id_; - string payload_; - bool is_muted_; - bool is_my_video_enabled_; - string invite_hash_; - int64 key_fingerprint_; + object_ptr input_group_call_; + object_ptr join_parameters_; joinGroupCall(); - joinGroupCall(int32 group_call_id_, object_ptr &&participant_id_, int32 audio_source_id_, string const &payload_, bool is_muted_, bool is_my_video_enabled_, string const &invite_hash_, int64 key_fingerprint_); + joinGroupCall(object_ptr &&input_group_call_, object_ptr &&join_parameters_); - static const std::int32_t ID = -1600024255; + static const std::int32_t ID = -2026079917; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class MessageSender; + +class groupCallJoinParameters; + +class text; + +class joinVideoChat final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + object_ptr participant_id_; + object_ptr join_parameters_; + string invite_hash_; + + joinVideoChat(); + + joinVideoChat(int32 group_call_id_, object_ptr &&participant_id_, object_ptr &&join_parameters_, string const &invite_hash_); + + static const std::int32_t ID = 1322989999; using ReturnType = object_ptr; @@ -43997,14 +44791,14 @@ class openStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; openStory(); - openStory(int53 story_sender_chat_id_, int32 story_id_); + openStory(int53 story_poster_chat_id_, int32 story_id_); - static const std::int32_t ID = -824542083; + static const std::int32_t ID = -696723005; using ReturnType = object_ptr; @@ -44162,6 +44956,45 @@ class pingProxy final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class InputStoryContent; + +class StoryPrivacySettings; + +class formattedText; + +class inputStoryAreas; + +class story; + +class storyFullId; + +class postStory final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 chat_id_; + object_ptr content_; + object_ptr areas_; + object_ptr caption_; + object_ptr privacy_settings_; + int32 active_period_; + object_ptr from_story_full_id_; + bool is_posted_to_chat_page_; + bool protect_content_; + + postStory(); + + postStory(int53 chat_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_, object_ptr &&privacy_settings_, int32 active_period_, object_ptr &&from_story_full_id_, bool is_posted_to_chat_page_, bool protect_content_); + + static const std::int32_t ID = -257605513; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class FileType; class InputFile; @@ -44433,7 +45266,7 @@ class readChatList final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class filePart; +class data; class readFilePart final : public Function { std::int32_t get_id() const final { @@ -44449,9 +45282,9 @@ class readFilePart final : public Function { readFilePart(int32 file_id_, int53 offset_, int53 count_); - static const std::int32_t ID = 906798861; + static const std::int32_t ID = -174576822; - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -45476,16 +46309,16 @@ class reportStory final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; bytes option_id_; string text_; reportStory(); - reportStory(int53 story_sender_chat_id_, int32 story_id_, bytes const &option_id_, string const &text_); + reportStory(int53 story_poster_chat_id_, int32 story_id_, bytes const &option_id_, string const &text_); - static const std::int32_t ID = 2027844368; + static const std::int32_t ID = 1823256372; using ReturnType = object_ptr; @@ -46213,6 +47046,35 @@ class searchFileDownloads final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class GiftForResaleOrder; + +class UpgradedGiftAttributeId; + +class giftsForResale; + +class searchGiftsForResale final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int64 gift_id_; + object_ptr order_; + array> attributes_; + string offset_; + int32 limit_; + + searchGiftsForResale(); + + searchGiftsForResale(int64 gift_id_, object_ptr &&order_, array> &&attributes_, string const &offset_, int32 limit_); + + static const std::int32_t ID = 1659300894; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class hashtags; class searchHashtags final : public Function { @@ -46414,16 +47276,16 @@ class searchPublicStoriesByTag final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; string tag_; string offset_; int32 limit_; searchPublicStoriesByTag(); - searchPublicStoriesByTag(int53 story_sender_chat_id_, string const &tag_, string const &offset_, int32 limit_); + searchPublicStoriesByTag(int53 story_poster_chat_id_, string const &tag_, string const &offset_, int32 limit_); - static const std::int32_t ID = -1563792893; + static const std::int32_t ID = 1778102602; using ReturnType = object_ptr; @@ -47263,41 +48125,27 @@ class sendQuickReplyShortcutMessages final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class InputStoryContent; +class MessageSender; -class StoryPrivacySettings; +class ok; -class formattedText; - -class inputStoryAreas; - -class story; - -class storyFullId; - -class sendStory final : public Function { +class sendResoldGift final : public Function { std::int32_t get_id() const final { return ID; } public: - int53 chat_id_; - object_ptr content_; - object_ptr areas_; - object_ptr caption_; - object_ptr privacy_settings_; - int32 active_period_; - object_ptr from_story_full_id_; - bool is_posted_to_chat_page_; - bool protect_content_; + string gift_name_; + object_ptr owner_id_; + int53 star_count_; - sendStory(); + sendResoldGift(); - sendStory(int53 chat_id_, object_ptr &&content_, object_ptr &&areas_, object_ptr &&caption_, object_ptr &&privacy_settings_, int32 active_period_, object_ptr &&from_story_full_id_, bool is_posted_to_chat_page_, bool protect_content_); + sendResoldGift(string const &gift_name_, object_ptr &&owner_id_, int53 star_count_); - static const std::int32_t ID = -424987902; + static const std::int32_t ID = -1284812948; - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerToString &s, const char *field_name) const final; }; @@ -48917,6 +49765,28 @@ class setGameScore final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; +class ok; + +class setGiftResalePrice final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + string received_gift_id_; + int53 resale_star_count_; + + setGiftResalePrice(); + + setGiftResalePrice(string const &received_gift_id_, int53 resale_star_count_); + + static const std::int32_t ID = -1430492787; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class giftSettings; class ok; @@ -48990,28 +49860,6 @@ class setGroupCallParticipantVolumeLevel final : public Function { class ok; -class setGroupCallTitle final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 group_call_id_; - string title_; - - setGroupCallTitle(); - - setGroupCallTitle(int32 group_call_id_, string const &title_); - - static const std::int32_t ID = -1228825139; - - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class ok; - class setInactiveSessionTtl final : public Function { std::int32_t get_id() const final { return ID; @@ -49962,16 +50810,16 @@ class setStoryReaction final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; object_ptr reaction_type_; bool update_recent_reactions_; setStoryReaction(); - setStoryReaction(int53 story_sender_chat_id_, int32 story_id_, object_ptr &&reaction_type_, bool update_recent_reactions_); + setStoryReaction(int53 story_poster_chat_id_, int32 story_id_, object_ptr &&reaction_type_, bool update_recent_reactions_); - static const std::int32_t ID = -1400156249; + static const std::int32_t ID = 250731529; using ReturnType = object_ptr; @@ -50245,6 +51093,28 @@ class setVideoChatDefaultParticipant final : public Function { class ok; +class setVideoChatTitle final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + string title_; + + setVideoChatTitle(); + + setVideoChatTitle(int32 group_call_id_, string const &title_); + + static const std::int32_t ID = 1915482994; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + class shareChatWithBot final : public Function { std::int32_t get_id() const final { return ID; @@ -50363,7 +51233,7 @@ class startGroupCallScreenSharing final : public Function { class ok; -class startScheduledGroupCall final : public Function { +class startScheduledVideoChat final : public Function { std::int32_t get_id() const final { return ID; } @@ -50371,11 +51241,11 @@ class startScheduledGroupCall final : public Function { public: int32 group_call_id_; - startScheduledGroupCall(); + startScheduledVideoChat(); - explicit startScheduledGroupCall(int32 group_call_id_); + explicit startScheduledVideoChat(int32 group_call_id_); - static const std::int32_t ID = 1519938838; + static const std::int32_t ID = -1300829822; using ReturnType = object_ptr; @@ -51191,28 +52061,6 @@ class toggleGiftIsSaved final : public Function { class ok; -class toggleGroupCallEnabledStartNotification final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 group_call_id_; - bool enabled_start_notification_; - - toggleGroupCallEnabledStartNotification(); - - toggleGroupCallEnabledStartNotification(int32 group_call_id_, bool enabled_start_notification_); - - static const std::int32_t ID = 707839826; - - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - -class ok; - class toggleGroupCallIsMyVideoEnabled final : public Function { std::int32_t get_id() const final { return ID; @@ -51255,28 +52103,6 @@ class toggleGroupCallIsMyVideoPaused final : public Function { void store(TlStorerToString &s, const char *field_name) const final; }; -class ok; - -class toggleGroupCallMuteNewParticipants final : public Function { - std::int32_t get_id() const final { - return ID; - } - - public: - int32 group_call_id_; - bool mute_new_participants_; - - toggleGroupCallMuteNewParticipants(); - - toggleGroupCallMuteNewParticipants(int32 group_call_id_, bool mute_new_participants_); - - static const std::int32_t ID = 284082626; - - using ReturnType = object_ptr; - - void store(TlStorerToString &s, const char *field_name) const final; -}; - class MessageSender; class ok; @@ -51444,15 +52270,15 @@ class toggleStoryIsPostedToChatPage final : public Function { } public: - int53 story_sender_chat_id_; + int53 story_poster_chat_id_; int32 story_id_; bool is_posted_to_chat_page_; toggleStoryIsPostedToChatPage(); - toggleStoryIsPostedToChatPage(int53 story_sender_chat_id_, int32 story_id_, bool is_posted_to_chat_page_); + toggleStoryIsPostedToChatPage(int53 story_poster_chat_id_, int32 story_id_, bool is_posted_to_chat_page_); - static const std::int32_t ID = -300987649; + static const std::int32_t ID = -2141806228; using ReturnType = object_ptr; @@ -51505,6 +52331,28 @@ class toggleSupergroupHasAggressiveAntiSpamEnabled final : public Function { class ok; +class toggleSupergroupHasAutomaticTranslation final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int53 supergroup_id_; + bool has_automatic_translation_; + + toggleSupergroupHasAutomaticTranslation(); + + toggleSupergroupHasAutomaticTranslation(int53 supergroup_id_, bool has_automatic_translation_); + + static const std::int32_t ID = -184993048; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + class toggleSupergroupHasHiddenMembers final : public Function { std::int32_t get_id() const final { return ID; @@ -51704,6 +52552,50 @@ class toggleUsernameIsActive final : public Function { class ok; +class toggleVideoChatEnabledStartNotification final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + bool enabled_start_notification_; + + toggleVideoChatEnabledStartNotification(); + + toggleVideoChatEnabledStartNotification(int32 group_call_id_, bool enabled_start_notification_); + + static const std::int32_t ID = 1851489086; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + +class toggleVideoChatMuteNewParticipants final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 group_call_id_; + bool mute_new_participants_; + + toggleVideoChatMuteNewParticipants(); + + toggleVideoChatMuteNewParticipants(int32 group_call_id_, bool mute_new_participants_); + + static const std::int32_t ID = 987023756; + + using ReturnType = object_ptr; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class ok; + class transferBusinessAccountStars final : public Function { std::int32_t get_id() const final { return ID; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/td_api_json.h b/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/td_api_json.h index 52d31007d5..5c72d2ba48 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/td_api_json.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/td_api_json.h @@ -64,6 +64,10 @@ Result tl_constructor_from_string(td_api::FileType *object, const std::st Result tl_constructor_from_string(td_api::FirebaseAuthenticationSettings *object, const std::string &str); +Result tl_constructor_from_string(td_api::GiftForResaleOrder *object, const std::string &str); + +Result tl_constructor_from_string(td_api::GroupCallDataChannel *object, const std::string &str); + Result tl_constructor_from_string(td_api::GroupCallVideoQuality *object, const std::string &str); Result tl_constructor_from_string(td_api::InlineKeyboardButtonType *object, const std::string &str); @@ -78,6 +82,8 @@ Result tl_constructor_from_string(td_api::InputCredentials *object, const Result tl_constructor_from_string(td_api::InputFile *object, const std::string &str); +Result tl_constructor_from_string(td_api::InputGroupCall *object, const std::string &str); + Result tl_constructor_from_string(td_api::InputInlineQueryResult *object, const std::string &str); Result tl_constructor_from_string(td_api::InputInvoice *object, const std::string &str); @@ -186,6 +192,8 @@ Result tl_constructor_from_string(td_api::TextParseMode *object, const st Result tl_constructor_from_string(td_api::TopChatCategory *object, const std::string &str); +Result tl_constructor_from_string(td_api::UpgradedGiftAttributeId *object, const std::string &str); + Result tl_constructor_from_string(td_api::UserPrivacySetting *object, const std::string &str); Result tl_constructor_from_string(td_api::UserPrivacySettingRule *object, const std::string &str); @@ -538,10 +546,22 @@ Status from_json(td_api::formattedText &to, JsonObject &from); Status from_json(td_api::forumTopicIcon &to, JsonObject &from); +Status from_json(td_api::giftForResaleOrderPrice &to, JsonObject &from); + +Status from_json(td_api::giftForResaleOrderPriceChangeDate &to, JsonObject &from); + +Status from_json(td_api::giftForResaleOrderNumber &to, JsonObject &from); + Status from_json(td_api::giftSettings &to, JsonObject &from); Status from_json(td_api::giveawayParameters &to, JsonObject &from); +Status from_json(td_api::groupCallDataChannelMain &to, JsonObject &from); + +Status from_json(td_api::groupCallDataChannelScreenSharing &to, JsonObject &from); + +Status from_json(td_api::groupCallJoinParameters &to, JsonObject &from); + Status from_json(td_api::groupCallVideoQualityThumbnail &to, JsonObject &from); Status from_json(td_api::groupCallVideoQualityMedium &to, JsonObject &from); @@ -610,6 +630,10 @@ Status from_json(td_api::inputFileLocal &to, JsonObject &from); Status from_json(td_api::inputFileGenerated &to, JsonObject &from); +Status from_json(td_api::inputGroupCallLink &to, JsonObject &from); + +Status from_json(td_api::inputGroupCallMessage &to, JsonObject &from); + Status from_json(td_api::inputIdentityDocument &to, JsonObject &from); Status from_json(td_api::inputInlineQueryResultAnimation &to, JsonObject &from); @@ -806,6 +830,8 @@ Status from_json(td_api::internalLinkTypeEditProfileSettings &to, JsonObject &fr Status from_json(td_api::internalLinkTypeGame &to, JsonObject &from); +Status from_json(td_api::internalLinkTypeGroupCall &to, JsonObject &from); + Status from_json(td_api::internalLinkTypeInstantView &to, JsonObject &from); Status from_json(td_api::internalLinkTypeInvoice &to, JsonObject &from); @@ -820,6 +846,8 @@ Status from_json(td_api::internalLinkTypeMessage &to, JsonObject &from); Status from_json(td_api::internalLinkTypeMessageDraft &to, JsonObject &from); +Status from_json(td_api::internalLinkTypeMyStars &to, JsonObject &from); + Status from_json(td_api::internalLinkTypePassportDataRequest &to, JsonObject &from); Status from_json(td_api::internalLinkTypePhoneNumberConfirmation &to, JsonObject &from); @@ -1124,9 +1152,9 @@ Status from_json(td_api::premiumLimitTypeShareableChatFolderCount &to, JsonObjec Status from_json(td_api::premiumLimitTypeActiveStoryCount &to, JsonObject &from); -Status from_json(td_api::premiumLimitTypeWeeklySentStoryCount &to, JsonObject &from); +Status from_json(td_api::premiumLimitTypeWeeklyPostedStoryCount &to, JsonObject &from); -Status from_json(td_api::premiumLimitTypeMonthlySentStoryCount &to, JsonObject &from); +Status from_json(td_api::premiumLimitTypeMonthlyPostedStoryCount &to, JsonObject &from); Status from_json(td_api::premiumLimitTypeStoryCaptionLength &to, JsonObject &from); @@ -1344,6 +1372,8 @@ Status from_json(td_api::suggestedActionExtendPremium &to, JsonObject &from); Status from_json(td_api::suggestedActionExtendStarSubscriptions &to, JsonObject &from); +Status from_json(td_api::suggestedActionCustom &to, JsonObject &from); + Status from_json(td_api::supergroupMembersFilterRecent &to, JsonObject &from); Status from_json(td_api::supergroupMembersFilterContacts &to, JsonObject &from); @@ -1454,6 +1484,12 @@ Status from_json(td_api::topChatCategoryCalls &to, JsonObject &from); Status from_json(td_api::topChatCategoryForwardChats &to, JsonObject &from); +Status from_json(td_api::upgradedGiftAttributeIdModel &to, JsonObject &from); + +Status from_json(td_api::upgradedGiftAttributeIdSymbol &to, JsonObject &from); + +Status from_json(td_api::upgradedGiftAttributeIdBackdrop &to, JsonObject &from); + Status from_json(td_api::upgradedGiftBackdropColors &to, JsonObject &from); Status from_json(td_api::userPrivacySettingShowStatus &to, JsonObject &from); @@ -1590,18 +1626,20 @@ Status from_json(td_api::assignStoreTransaction &to, JsonObject &from); Status from_json(td_api::banChatMember &to, JsonObject &from); +Status from_json(td_api::banGroupCallParticipants &to, JsonObject &from); + Status from_json(td_api::blockMessageSenderFromReplies &to, JsonObject &from); Status from_json(td_api::boostChat &to, JsonObject &from); Status from_json(td_api::canBotSendMessages &to, JsonObject &from); +Status from_json(td_api::canPostStory &to, JsonObject &from); + Status from_json(td_api::canPurchaseFromStore &to, JsonObject &from); Status from_json(td_api::canSendMessageToUser &to, JsonObject &from); -Status from_json(td_api::canSendStory &to, JsonObject &from); - Status from_json(td_api::canTransferOwnership &to, JsonObject &from); Status from_json(td_api::cancelDownloadFile &to, JsonObject &from); @@ -1734,6 +1772,10 @@ Status from_json(td_api::createTemporaryPassword &to, JsonObject &from); Status from_json(td_api::createVideoChat &to, JsonObject &from); +Status from_json(td_api::declineGroupCallInvitation &to, JsonObject &from); + +Status from_json(td_api::decryptGroupCallData &to, JsonObject &from); + Status from_json(td_api::deleteAccount &to, JsonObject &from); Status from_json(td_api::deleteAllCallMessages &to, JsonObject &from); @@ -1880,6 +1922,8 @@ Status from_json(td_api::editUserStarSubscription &to, JsonObject &from); Status from_json(td_api::enableProxy &to, JsonObject &from); +Status from_json(td_api::encryptGroupCallData &to, JsonObject &from); + Status from_json(td_api::endGroupCall &to, JsonObject &from); Status from_json(td_api::endGroupCallRecording &to, JsonObject &from); @@ -2054,7 +2098,7 @@ Status from_json(td_api::getChats &to, JsonObject &from); Status from_json(td_api::getChatsForChatFolderInviteLink &to, JsonObject &from); -Status from_json(td_api::getChatsToSendStories &to, JsonObject &from); +Status from_json(td_api::getChatsToPostStories &to, JsonObject &from); Status from_json(td_api::getCloseFriends &to, JsonObject &from); @@ -2144,11 +2188,7 @@ Status from_json(td_api::getGrossingWebAppBots &to, JsonObject &from); Status from_json(td_api::getGroupCall &to, JsonObject &from); -Status from_json(td_api::getGroupCallInviteLink &to, JsonObject &from); - -Status from_json(td_api::getGroupCallStreamSegment &to, JsonObject &from); - -Status from_json(td_api::getGroupCallStreams &to, JsonObject &from); +Status from_json(td_api::getGroupCallParticipants &to, JsonObject &from); Status from_json(td_api::getGroupsInCommon &to, JsonObject &from); @@ -2446,8 +2486,14 @@ Status from_json(td_api::getUserSupportInfo &to, JsonObject &from); Status from_json(td_api::getVideoChatAvailableParticipants &to, JsonObject &from); +Status from_json(td_api::getVideoChatInviteLink &to, JsonObject &from); + Status from_json(td_api::getVideoChatRtmpUrl &to, JsonObject &from); +Status from_json(td_api::getVideoChatStreamSegment &to, JsonObject &from); + +Status from_json(td_api::getVideoChatStreams &to, JsonObject &from); + Status from_json(td_api::getWebAppLinkUrl &to, JsonObject &from); Status from_json(td_api::getWebAppPlaceholder &to, JsonObject &from); @@ -2466,7 +2512,9 @@ Status from_json(td_api::importContacts &to, JsonObject &from); Status from_json(td_api::importMessages &to, JsonObject &from); -Status from_json(td_api::inviteGroupCallParticipants &to, JsonObject &from); +Status from_json(td_api::inviteGroupCallParticipant &to, JsonObject &from); + +Status from_json(td_api::inviteVideoChatParticipants &to, JsonObject &from); Status from_json(td_api::joinChat &to, JsonObject &from); @@ -2474,6 +2522,8 @@ Status from_json(td_api::joinChatByInviteLink &to, JsonObject &from); Status from_json(td_api::joinGroupCall &to, JsonObject &from); +Status from_json(td_api::joinVideoChat &to, JsonObject &from); + Status from_json(td_api::launchPrepaidGiveaway &to, JsonObject &from); Status from_json(td_api::leaveChat &to, JsonObject &from); @@ -2518,6 +2568,8 @@ Status from_json(td_api::pinChatMessage &to, JsonObject &from); Status from_json(td_api::pingProxy &to, JsonObject &from); +Status from_json(td_api::postStory &to, JsonObject &from); + Status from_json(td_api::preliminaryUploadFile &to, JsonObject &from); Status from_json(td_api::processChatFolderNewChats &to, JsonObject &from); @@ -2702,6 +2754,8 @@ Status from_json(td_api::searchEmojis &to, JsonObject &from); Status from_json(td_api::searchFileDownloads &to, JsonObject &from); +Status from_json(td_api::searchGiftsForResale &to, JsonObject &from); + Status from_json(td_api::searchHashtags &to, JsonObject &from); Status from_json(td_api::searchInstalledStickerSets &to, JsonObject &from); @@ -2786,7 +2840,7 @@ Status from_json(td_api::sendPhoneNumberFirebaseSms &to, JsonObject &from); Status from_json(td_api::sendQuickReplyShortcutMessages &to, JsonObject &from); -Status from_json(td_api::sendStory &to, JsonObject &from); +Status from_json(td_api::sendResoldGift &to, JsonObject &from); Status from_json(td_api::sendWebAppCustomRequest &to, JsonObject &from); @@ -2926,14 +2980,14 @@ Status from_json(td_api::setForumTopicNotificationSettings &to, JsonObject &from Status from_json(td_api::setGameScore &to, JsonObject &from); +Status from_json(td_api::setGiftResalePrice &to, JsonObject &from); + Status from_json(td_api::setGiftSettings &to, JsonObject &from); Status from_json(td_api::setGroupCallParticipantIsSpeaking &to, JsonObject &from); Status from_json(td_api::setGroupCallParticipantVolumeLevel &to, JsonObject &from); -Status from_json(td_api::setGroupCallTitle &to, JsonObject &from); - Status from_json(td_api::setInactiveSessionTtl &to, JsonObject &from); Status from_json(td_api::setInlineGameScore &to, JsonObject &from); @@ -3038,6 +3092,8 @@ Status from_json(td_api::setUsername &to, JsonObject &from); Status from_json(td_api::setVideoChatDefaultParticipant &to, JsonObject &from); +Status from_json(td_api::setVideoChatTitle &to, JsonObject &from); + Status from_json(td_api::shareChatWithBot &to, JsonObject &from); Status from_json(td_api::sharePhoneNumber &to, JsonObject &from); @@ -3048,7 +3104,7 @@ Status from_json(td_api::startGroupCallRecording &to, JsonObject &from); Status from_json(td_api::startGroupCallScreenSharing &to, JsonObject &from); -Status from_json(td_api::startScheduledGroupCall &to, JsonObject &from); +Status from_json(td_api::startScheduledVideoChat &to, JsonObject &from); Status from_json(td_api::stopBusinessPoll &to, JsonObject &from); @@ -3124,14 +3180,10 @@ Status from_json(td_api::toggleGeneralForumTopicIsHidden &to, JsonObject &from); Status from_json(td_api::toggleGiftIsSaved &to, JsonObject &from); -Status from_json(td_api::toggleGroupCallEnabledStartNotification &to, JsonObject &from); - Status from_json(td_api::toggleGroupCallIsMyVideoEnabled &to, JsonObject &from); Status from_json(td_api::toggleGroupCallIsMyVideoPaused &to, JsonObject &from); -Status from_json(td_api::toggleGroupCallMuteNewParticipants &to, JsonObject &from); - Status from_json(td_api::toggleGroupCallParticipantIsHandRaised &to, JsonObject &from); Status from_json(td_api::toggleGroupCallParticipantIsMuted &to, JsonObject &from); @@ -3152,6 +3204,8 @@ Status from_json(td_api::toggleSupergroupCanHaveSponsoredMessages &to, JsonObjec Status from_json(td_api::toggleSupergroupHasAggressiveAntiSpamEnabled &to, JsonObject &from); +Status from_json(td_api::toggleSupergroupHasAutomaticTranslation &to, JsonObject &from); + Status from_json(td_api::toggleSupergroupHasHiddenMembers &to, JsonObject &from); Status from_json(td_api::toggleSupergroupIsAllHistoryAvailable &to, JsonObject &from); @@ -3170,6 +3224,10 @@ Status from_json(td_api::toggleSupergroupUsernameIsActive &to, JsonObject &from) Status from_json(td_api::toggleUsernameIsActive &to, JsonObject &from); +Status from_json(td_api::toggleVideoChatEnabledStartNotification &to, JsonObject &from); + +Status from_json(td_api::toggleVideoChatMuteNewParticipants &to, JsonObject &from); + Status from_json(td_api::transferBusinessAccountStars &to, JsonObject &from); Status from_json(td_api::transferChatOwnership &to, JsonObject &from); @@ -3312,6 +3370,10 @@ void to_json(JsonValueScope &jv, const td_api::autosaveSettingsScopeChannelChats void to_json(JsonValueScope &jv, const td_api::autosaveSettingsScopeChat &object); +void to_json(JsonValueScope &jv, const td_api::availableGift &object); + +void to_json(JsonValueScope &jv, const td_api::availableGifts &object); + void to_json(JsonValueScope &jv, const td_api::availableReaction &object); void to_json(JsonValueScope &jv, const td_api::availableReactions &object); @@ -3466,7 +3528,7 @@ void to_json(JsonValueScope &jv, const td_api::callDiscardReasonDisconnected &ob void to_json(JsonValueScope &jv, const td_api::callDiscardReasonHungUp &object); -void to_json(JsonValueScope &jv, const td_api::callDiscardReasonAllowGroupCall &object); +void to_json(JsonValueScope &jv, const td_api::callDiscardReasonUpgradeToGroupCall &object); void to_json(JsonValueScope &jv, const td_api::callId &object); @@ -3504,6 +3566,20 @@ void to_json(JsonValueScope &jv, const td_api::callbackQueryPayloadDataWithPassw void to_json(JsonValueScope &jv, const td_api::callbackQueryPayloadGame &object); +void to_json(JsonValueScope &jv, const td_api::CanPostStoryResult &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultOk &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultPremiumNeeded &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultBoostNeeded &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultActiveStoryLimitExceeded &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultWeeklyLimitExceeded &object); + +void to_json(JsonValueScope &jv, const td_api::canPostStoryResultMonthlyLimitExceeded &object); + void to_json(JsonValueScope &jv, const td_api::CanSendMessageToUserResult &object); void to_json(JsonValueScope &jv, const td_api::canSendMessageToUserResultOk &object); @@ -3514,20 +3590,6 @@ void to_json(JsonValueScope &jv, const td_api::canSendMessageToUserResultUserIsD void to_json(JsonValueScope &jv, const td_api::canSendMessageToUserResultUserRestrictsNewChats &object); -void to_json(JsonValueScope &jv, const td_api::CanSendStoryResult &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultOk &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultPremiumNeeded &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultBoostNeeded &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultActiveStoryLimitExceeded &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultWeeklyLimitExceeded &object); - -void to_json(JsonValueScope &jv, const td_api::canSendStoryResultMonthlyLimitExceeded &object); - void to_json(JsonValueScope &jv, const td_api::CanTransferOwnershipResult &object); void to_json(JsonValueScope &jv, const td_api::canTransferOwnershipResultOk &object); @@ -3702,6 +3764,8 @@ void to_json(JsonValueScope &jv, const td_api::chatEventSignMessagesToggled &obj void to_json(JsonValueScope &jv, const td_api::chatEventShowMessageSenderToggled &object); +void to_json(JsonValueScope &jv, const td_api::chatEventAutomaticTranslationToggled &object); + void to_json(JsonValueScope &jv, const td_api::chatEventInviteLinkEdited &object); void to_json(JsonValueScope &jv, const td_api::chatEventInviteLinkRevoked &object); @@ -3942,6 +4006,8 @@ void to_json(JsonValueScope &jv, const td_api::currentWeather &object); void to_json(JsonValueScope &jv, const td_api::customRequestResult &object); +void to_json(JsonValueScope &jv, const td_api::data &object); + void to_json(JsonValueScope &jv, const td_api::databaseStatistics &object); void to_json(JsonValueScope &jv, const td_api::date &object); @@ -4020,8 +4086,6 @@ void to_json(JsonValueScope &jv, const td_api::fileDownload &object); void to_json(JsonValueScope &jv, const td_api::fileDownloadedPrefixSize &object); -void to_json(JsonValueScope &jv, const td_api::filePart &object); - void to_json(JsonValueScope &jv, const td_api::FileType &object); void to_json(JsonValueScope &jv, const td_api::fileTypeNone &object); @@ -4118,11 +4182,13 @@ void to_json(JsonValueScope &jv, const td_api::gameHighScores &object); void to_json(JsonValueScope &jv, const td_api::gift &object); +void to_json(JsonValueScope &jv, const td_api::giftForResale &object); + void to_json(JsonValueScope &jv, const td_api::giftSettings &object); void to_json(JsonValueScope &jv, const td_api::giftUpgradePreview &object); -void to_json(JsonValueScope &jv, const td_api::gifts &object); +void to_json(JsonValueScope &jv, const td_api::giftsForResale &object); void to_json(JsonValueScope &jv, const td_api::GiveawayInfo &object); @@ -4154,16 +4220,16 @@ void to_json(JsonValueScope &jv, const td_api::groupCall &object); void to_json(JsonValueScope &jv, const td_api::groupCallId &object); +void to_json(JsonValueScope &jv, const td_api::groupCallInfo &object); + void to_json(JsonValueScope &jv, const td_api::groupCallParticipant &object); void to_json(JsonValueScope &jv, const td_api::groupCallParticipantVideoInfo &object); +void to_json(JsonValueScope &jv, const td_api::groupCallParticipants &object); + void to_json(JsonValueScope &jv, const td_api::groupCallRecentSpeaker &object); -void to_json(JsonValueScope &jv, const td_api::groupCallStream &object); - -void to_json(JsonValueScope &jv, const td_api::groupCallStreams &object); - void to_json(JsonValueScope &jv, const td_api::groupCallVideoSourceGroup &object); void to_json(JsonValueScope &jv, const td_api::hashtags &object); @@ -4342,6 +4408,8 @@ void to_json(JsonValueScope &jv, const td_api::internalLinkTypeEditProfileSettin void to_json(JsonValueScope &jv, const td_api::internalLinkTypeGame &object); +void to_json(JsonValueScope &jv, const td_api::internalLinkTypeGroupCall &object); + void to_json(JsonValueScope &jv, const td_api::internalLinkTypeInstantView &object); void to_json(JsonValueScope &jv, const td_api::internalLinkTypeInvoice &object); @@ -4356,6 +4424,8 @@ void to_json(JsonValueScope &jv, const td_api::internalLinkTypeMessage &object); void to_json(JsonValueScope &jv, const td_api::internalLinkTypeMessageDraft &object); +void to_json(JsonValueScope &jv, const td_api::internalLinkTypeMyStars &object); + void to_json(JsonValueScope &jv, const td_api::internalLinkTypePassportDataRequest &object); void to_json(JsonValueScope &jv, const td_api::internalLinkTypePhoneNumberConfirmation &object); @@ -4400,6 +4470,16 @@ void to_json(JsonValueScope &jv, const td_api::internalLinkTypeVideoChat &object void to_json(JsonValueScope &jv, const td_api::internalLinkTypeWebApp &object); +void to_json(JsonValueScope &jv, const td_api::InviteGroupCallParticipantResult &object); + +void to_json(JsonValueScope &jv, const td_api::inviteGroupCallParticipantResultUserPrivacyRestricted &object); + +void to_json(JsonValueScope &jv, const td_api::inviteGroupCallParticipantResultUserAlreadyParticipant &object); + +void to_json(JsonValueScope &jv, const td_api::inviteGroupCallParticipantResultUserWasBanned &object); + +void to_json(JsonValueScope &jv, const td_api::inviteGroupCallParticipantResultSuccess &object); + void to_json(JsonValueScope &jv, const td_api::InviteLinkChatType &object); void to_json(JsonValueScope &jv, const td_api::inviteLinkChatTypeBasicGroup &object); @@ -4500,6 +4580,8 @@ void to_json(JsonValueScope &jv, const td_api::linkPreviewTypeExternalAudio &obj void to_json(JsonValueScope &jv, const td_api::linkPreviewTypeExternalVideo &object); +void to_json(JsonValueScope &jv, const td_api::linkPreviewTypeGroupCall &object); + void to_json(JsonValueScope &jv, const td_api::linkPreviewTypeInvoice &object); void to_json(JsonValueScope &jv, const td_api::linkPreviewTypeMessage &object); @@ -4634,6 +4716,8 @@ void to_json(JsonValueScope &jv, const td_api::messageInvoice &object); void to_json(JsonValueScope &jv, const td_api::messageCall &object); +void to_json(JsonValueScope &jv, const td_api::messageGroupCall &object); + void to_json(JsonValueScope &jv, const td_api::messageVideoChatScheduled &object); void to_json(JsonValueScope &jv, const td_api::messageVideoChatStarted &object); @@ -5268,9 +5352,9 @@ void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeShareableChatFold void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeActiveStoryCount &object); -void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeWeeklySentStoryCount &object); +void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeWeeklyPostedStoryCount &object); -void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeMonthlySentStoryCount &object); +void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeMonthlyPostedStoryCount &object); void to_json(JsonValueScope &jv, const td_api::premiumLimitTypeStoryCaptionLength &object); @@ -5726,6 +5810,10 @@ void to_json(JsonValueScope &jv, const td_api::starTransactionTypeGiftSale &obje void to_json(JsonValueScope &jv, const td_api::starTransactionTypeGiftUpgrade &object); +void to_json(JsonValueScope &jv, const td_api::starTransactionTypeUpgradedGiftPurchase &object); + +void to_json(JsonValueScope &jv, const td_api::starTransactionTypeUpgradedGiftSale &object); + void to_json(JsonValueScope &jv, const td_api::starTransactionTypeChannelPaidReactionSend &object); void to_json(JsonValueScope &jv, const td_api::starTransactionTypeChannelPaidReactionReceive &object); @@ -5904,6 +5992,8 @@ void to_json(JsonValueScope &jv, const td_api::suggestedActionExtendPremium &obj void to_json(JsonValueScope &jv, const td_api::suggestedActionExtendStarSubscriptions &object); +void to_json(JsonValueScope &jv, const td_api::suggestedActionCustom &object); + void to_json(JsonValueScope &jv, const td_api::supergroup &object); void to_json(JsonValueScope &jv, const td_api::supergroupFullInfo &object); @@ -6210,6 +6300,10 @@ void to_json(JsonValueScope &jv, const td_api::updateGroupCall &object); void to_json(JsonValueScope &jv, const td_api::updateGroupCallParticipant &object); +void to_json(JsonValueScope &jv, const td_api::updateGroupCallParticipants &object); + +void to_json(JsonValueScope &jv, const td_api::updateGroupCallVerificationState &object); + void to_json(JsonValueScope &jv, const td_api::updateNewCallSignalingData &object); void to_json(JsonValueScope &jv, const td_api::updateUserPrivacySettingRules &object); @@ -6222,9 +6316,9 @@ void to_json(JsonValueScope &jv, const td_api::updateStory &object); void to_json(JsonValueScope &jv, const td_api::updateStoryDeleted &object); -void to_json(JsonValueScope &jv, const td_api::updateStorySendSucceeded &object); +void to_json(JsonValueScope &jv, const td_api::updateStoryPostSucceeded &object); -void to_json(JsonValueScope &jv, const td_api::updateStorySendFailed &object); +void to_json(JsonValueScope &jv, const td_api::updateStoryPostFailed &object); void to_json(JsonValueScope &jv, const td_api::updateChatActiveStories &object); @@ -6356,12 +6450,18 @@ void to_json(JsonValueScope &jv, const td_api::upgradedGiftBackdrop &object); void to_json(JsonValueScope &jv, const td_api::upgradedGiftBackdropColors &object); +void to_json(JsonValueScope &jv, const td_api::upgradedGiftBackdropCount &object); + void to_json(JsonValueScope &jv, const td_api::upgradedGiftModel &object); +void to_json(JsonValueScope &jv, const td_api::upgradedGiftModelCount &object); + void to_json(JsonValueScope &jv, const td_api::upgradedGiftOriginalDetails &object); void to_json(JsonValueScope &jv, const td_api::upgradedGiftSymbol &object); +void to_json(JsonValueScope &jv, const td_api::upgradedGiftSymbolCount &object); + void to_json(JsonValueScope &jv, const td_api::user &object); void to_json(JsonValueScope &jv, const td_api::userFullInfo &object); @@ -6468,6 +6568,10 @@ void to_json(JsonValueScope &jv, const td_api::video &object); void to_json(JsonValueScope &jv, const td_api::videoChat &object); +void to_json(JsonValueScope &jv, const td_api::videoChatStream &object); + +void to_json(JsonValueScope &jv, const td_api::videoChatStreams &object); + void to_json(JsonValueScope &jv, const td_api::videoNote &object); void to_json(JsonValueScope &jv, const td_api::voiceNote &object); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/telegram_api.h b/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/telegram_api.h index c35feeffb3..ff9514f8c7 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/telegram_api.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/generate/auto/td/telegram/telegram_api.h @@ -3,6 +3,7 @@ #include "td/tl/TlObject.h" #include "td/utils/buffer.h" +#include "td/utils/UInt.h" #include #include @@ -388,7 +389,6 @@ class autoDownloadSettings final : public Object { int32 video_upload_maxbitrate_; int32 small_queue_active_operations_max_; int32 large_queue_active_operations_max_; - enum Flags : std::int32_t { DISABLED_MASK = 1, VIDEO_PRELOAD_LARGE_MASK = 2, AUDIO_PRELOAD_NEXT_MASK = 4, PHONECALLS_LESS_DATA_MASK = 8, STORIES_PRELOAD_MASK = 16 }; autoDownloadSettings(); @@ -437,7 +437,7 @@ class autoSaveSettings final : public Object { bool photos_; bool videos_; int64 video_max_size_; - enum Flags : std::int32_t { PHOTOS_MASK = 1, VIDEOS_MASK = 2, VIDEO_MAX_SIZE_MASK = 4 }; + enum Flags : std::int32_t { VIDEO_MAX_SIZE_MASK = 4 }; autoSaveSettings(); @@ -1564,7 +1564,6 @@ class businessBotRights final : public Object { bool transfer_and_upgrade_gifts_; bool transfer_stars_; bool manage_stories_; - enum Flags : std::int32_t { REPLY_MASK = 1, READ_MESSAGES_MASK = 2, DELETE_SENT_MESSAGES_MASK = 4, DELETE_RECEIVED_MESSAGES_MASK = 8, EDIT_NAME_MASK = 16, EDIT_BIO_MASK = 32, EDIT_PROFILE_PHOTO_MASK = 64, EDIT_USERNAME_MASK = 128, VIEW_GIFTS_MASK = 256, SELL_GIFTS_MASK = 512, CHANGE_GIFT_SETTINGS_MASK = 1024, TRANSFER_AND_UPGRADE_GIFTS_MASK = 2048, TRANSFER_STARS_MASK = 4096, MANAGE_STORIES_MASK = 8192 }; businessBotRights(); @@ -1729,7 +1728,6 @@ class businessWorkHours final : public Object { bool open_now_; string timezone_id_; array> weekly_open_; - enum Flags : std::int32_t { OPEN_NOW_MASK = 1 }; businessWorkHours(); @@ -1817,6 +1815,8 @@ class ExportedChatInvite; class ForumTopic; +class InputGroupCall; + class InputStickerSet; class Message; @@ -1829,8 +1829,6 @@ class chatBannedRights; class groupCallParticipant; -class inputGroupCall; - class peerColor; class ChannelAdminLogEventAction: public Object { @@ -2208,7 +2206,7 @@ class channelAdminLogEventActionStartGroupCall final : public ChannelAdminLogEve } public: - object_ptr call_; + object_ptr call_; static const std::int32_t ID = 589338437; @@ -2225,7 +2223,7 @@ class channelAdminLogEventActionDiscardGroupCall final : public ChannelAdminLogE } public: - object_ptr call_; + object_ptr call_; static const std::int32_t ID = -610299584; @@ -2711,6 +2709,23 @@ class channelAdminLogEventActionParticipantSubExtend final : public ChannelAdmin void store(TlStorerToString &s, const char *field_name) const final; }; +class channelAdminLogEventActionToggleAutotranslation final : public ChannelAdminLogEventAction { + std::int32_t get_id() const final { + return ID; + } + + public: + bool new_value_; + + static const std::int32_t ID = -988285058; + + static object_ptr fetch(TlBufferParser &p); + + explicit channelAdminLogEventActionToggleAutotranslation(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class channelAdminLogEventsFilter final : public Object { std::int32_t get_id() const final { return ID; @@ -2737,7 +2752,6 @@ class channelAdminLogEventsFilter final : public Object { bool send_; bool forums_; bool sub_extend_; - enum Flags : std::int32_t { JOIN_MASK = 1, LEAVE_MASK = 2, INVITE_MASK = 4, BAN_MASK = 8, UNBAN_MASK = 16, KICK_MASK = 32, UNKICK_MASK = 64, PROMOTE_MASK = 128, DEMOTE_MASK = 256, INFO_MASK = 512, SETTINGS_MASK = 1024, PINNED_MASK = 2048, EDIT_MASK = 4096, DELETE_MASK = 8192, GROUP_CALL_MASK = 16384, INVITES_MASK = 32768, SEND_MASK = 65536, FORUMS_MASK = 131072, SUB_EXTEND_MASK = 262144 }; channelAdminLogEventsFilter(int32 flags_, bool join_, bool leave_, bool invite_, bool ban_, bool unban_, bool kick_, bool unkick_, bool promote_, bool demote_, bool info_, bool settings_, bool pinned_, bool edit_, bool delete_, bool group_call_, bool invites_, bool send_, bool forums_, bool sub_extend_); @@ -2821,7 +2835,6 @@ class channelMessagesFilter final : public ChannelMessagesFilter { int32 flags_; bool exclude_new_messages_; array> ranges_; - enum Flags : std::int32_t { EXCLUDE_NEW_MESSAGES_MASK = 2 }; channelMessagesFilter(int32 flags_, bool exclude_new_messages_, array> &&ranges_); @@ -3247,6 +3260,7 @@ class channel final : public Chat { bool stories_hidden_min_; bool stories_unavailable_; bool signature_profiles_; + bool autotranslation_; int64 id_; int64 access_hash_; string title_; @@ -3326,7 +3340,6 @@ class chatAdminRights final : public Object { bool post_stories_; bool edit_stories_; bool delete_stories_; - enum Flags : std::int32_t { CHANGE_INFO_MASK = 1, POST_MESSAGES_MASK = 2, EDIT_MESSAGES_MASK = 4, DELETE_MESSAGES_MASK = 8, BAN_USERS_MASK = 16, INVITE_USERS_MASK = 32, PIN_MESSAGES_MASK = 128, ADD_ADMINS_MASK = 512, ANONYMOUS_MASK = 1024, MANAGE_CALL_MASK = 2048, OTHER_MASK = 4096, MANAGE_TOPICS_MASK = 8192, POST_STORIES_MASK = 16384, EDIT_STORIES_MASK = 32768, DELETE_STORIES_MASK = 65536 }; chatAdminRights(); @@ -3390,7 +3403,6 @@ class chatBannedRights final : public Object { bool send_docs_; bool send_plain_; int32 until_date_; - enum Flags : std::int32_t { VIEW_MESSAGES_MASK = 1, SEND_MESSAGES_MASK = 2, SEND_MEDIA_MASK = 4, SEND_STICKERS_MASK = 8, SEND_GIFS_MASK = 16, SEND_GAMES_MASK = 32, SEND_INLINE_MASK = 64, EMBED_LINKS_MASK = 128, SEND_POLLS_MASK = 256, CHANGE_INFO_MASK = 1024, INVITE_USERS_MASK = 32768, PIN_MESSAGES_MASK = 131072, MANAGE_TOPICS_MASK = 262144, SEND_PHOTOS_MASK = 524288, SEND_VIDEOS_MASK = 1048576, SEND_ROUNDVIDEOS_MASK = 2097152, SEND_AUDIOS_MASK = 4194304, SEND_VOICES_MASK = 8388608, SEND_DOCS_MASK = 16777216, SEND_PLAIN_MASK = 33554432 }; chatBannedRights(); @@ -3415,6 +3427,8 @@ class ChatReactions; class ExportedChatInvite; +class InputGroupCall; + class Peer; class Photo; @@ -3425,8 +3439,6 @@ class botInfo; class botVerification; -class inputGroupCall; - class peerNotifySettings; class peerStories; @@ -3458,7 +3470,7 @@ class chatFull final : public ChatFull { array> bot_info_; int32 pinned_msg_id_; int32 folder_id_; - object_ptr call_; + object_ptr call_; int32 ttl_period_; object_ptr groupcall_default_join_as_; string theme_emoticon_; @@ -3532,7 +3544,7 @@ class channelFull final : public ChatFull { int32 slowmode_next_send_date_; int32 stats_dc_; int32 pts_; - object_ptr call_; + object_ptr call_; int32 ttl_period_; array pending_suggestions_; object_ptr groupcall_default_join_as_; @@ -3871,7 +3883,6 @@ class chatReactionsAll final : public ChatReactions { public: int32 flags_; bool allow_custom_; - enum Flags : std::int32_t { ALLOW_CUSTOM_MASK = 1 }; chatReactionsAll(); @@ -3927,7 +3938,7 @@ class codeSettings final : public Object { array logout_tokens_; string token_; bool app_sandbox_; - enum Flags : std::int32_t { ALLOW_FLASHCALL_MASK = 1, CURRENT_NUMBER_MASK = 2, ALLOW_APP_HASH_MASK = 16, ALLOW_MISSED_CALL_MASK = 32, ALLOW_FIREBASE_MASK = 128, UNKNOWN_NUMBER_MASK = 512, LOGOUT_TOKENS_MASK = 64, TOKEN_MASK = 256, APP_SANDBOX_MASK = 256 }; + enum Flags : std::int32_t { LOGOUT_TOKENS_MASK = 64, TOKEN_MASK = 256, APP_SANDBOX_MASK = 256 }; codeSettings(int32 flags_, bool allow_flashcall_, bool current_number_, bool allow_app_hash_, bool allow_missed_call_, bool allow_firebase_, bool unknown_number_, array &&logout_tokens_, string const &token_, bool app_sandbox_); @@ -4290,7 +4301,7 @@ class dialogFilter final : public DialogFilter { array> pinned_peers_; array> include_peers_; array> exclude_peers_; - enum Flags : std::int32_t { CONTACTS_MASK = 1, NON_CONTACTS_MASK = 2, GROUPS_MASK = 4, BROADCASTS_MASK = 8, BOTS_MASK = 16, EXCLUDE_MUTED_MASK = 2048, EXCLUDE_READ_MASK = 4096, EXCLUDE_ARCHIVED_MASK = 8192, TITLE_NOANIMATE_MASK = 268435456, EMOTICON_MASK = 33554432, COLOR_MASK = 134217728 }; + enum Flags : std::int32_t { EMOTICON_MASK = 33554432, COLOR_MASK = 134217728 }; dialogFilter(); @@ -4340,7 +4351,7 @@ class dialogFilterChatlist final : public DialogFilter { int32 color_; array> pinned_peers_; array> include_peers_; - enum Flags : std::int32_t { HAS_MY_INVITES_MASK = 67108864, TITLE_NOANIMATE_MASK = 268435456, EMOTICON_MASK = 33554432, COLOR_MASK = 134217728 }; + enum Flags : std::int32_t { EMOTICON_MASK = 33554432, COLOR_MASK = 134217728 }; dialogFilterChatlist(); @@ -4430,7 +4441,6 @@ class disallowedGiftsSettings final : public Object { bool disallow_limited_stargifts_; bool disallow_unique_stargifts_; bool disallow_premium_gifts_; - enum Flags : std::int32_t { DISALLOW_UNLIMITED_STARGIFTS_MASK = 1, DISALLOW_LIMITED_STARGIFTS_MASK = 2, DISALLOW_UNIQUE_STARGIFTS_MASK = 4, DISALLOW_PREMIUM_GIFTS_MASK = 8 }; disallowedGiftsSettings(); @@ -4568,7 +4578,7 @@ class documentAttributeSticker final : public DocumentAttribute { string alt_; object_ptr stickerset_; object_ptr mask_coords_; - enum Flags : std::int32_t { MASK_MASK = 2, MASK_COORDS_MASK = 1 }; + enum Flags : std::int32_t { MASK_COORDS_MASK = 1 }; documentAttributeSticker(); @@ -4601,7 +4611,7 @@ class documentAttributeVideo final : public DocumentAttribute { int32 preload_prefix_size_; double video_start_ts_; string video_codec_; - enum Flags : std::int32_t { ROUND_MESSAGE_MASK = 1, SUPPORTS_STREAMING_MASK = 2, NOSOUND_MASK = 8, PRELOAD_PREFIX_SIZE_MASK = 4, VIDEO_START_TS_MASK = 16, VIDEO_CODEC_MASK = 32 }; + enum Flags : std::int32_t { PRELOAD_PREFIX_SIZE_MASK = 4, VIDEO_START_TS_MASK = 16, VIDEO_CODEC_MASK = 32 }; documentAttributeVideo(); @@ -4630,7 +4640,7 @@ class documentAttributeAudio final : public DocumentAttribute { string title_; string performer_; bytes waveform_; - enum Flags : std::int32_t { VOICE_MASK = 1024, TITLE_MASK = 1, PERFORMER_MASK = 2, WAVEFORM_MASK = 4 }; + enum Flags : std::int32_t { TITLE_MASK = 1, PERFORMER_MASK = 2, WAVEFORM_MASK = 4 }; documentAttributeAudio(); @@ -4699,7 +4709,6 @@ class documentAttributeCustomEmoji final : public DocumentAttribute { bool text_color_; string alt_; object_ptr stickerset_; - enum Flags : std::int32_t { FREE_MASK = 1, TEXT_COLOR_MASK = 2 }; documentAttributeCustomEmoji(); @@ -5853,7 +5862,7 @@ class globalPrivacySettings final : public Object { bool display_gifts_button_; int64 noncontact_peers_paid_stars_; object_ptr disallowed_gifts_; - enum Flags : std::int32_t { ARCHIVE_AND_MUTE_NEW_NONCONTACT_PEERS_MASK = 1, KEEP_ARCHIVED_UNMUTED_MASK = 2, KEEP_ARCHIVED_FOLDERS_MASK = 4, HIDE_READ_MARKS_MASK = 8, NEW_NONCONTACT_PEERS_REQUIRE_PREMIUM_MASK = 16, DISPLAY_GIFTS_BUTTON_MASK = 128, NONCONTACT_PEERS_PAID_STARS_MASK = 32, DISALLOWED_GIFTS_MASK = 64 }; + enum Flags : std::int32_t { NONCONTACT_PEERS_PAID_STARS_MASK = 32, DISALLOWED_GIFTS_MASK = 64 }; globalPrivacySettings(); @@ -5910,6 +5919,8 @@ class groupCall final : public GroupCall { bool record_video_active_; bool rtmp_stream_; bool listeners_hidden_; + bool conference_; + bool creator_; int64 id_; int64 access_hash_; int32 participants_count_; @@ -5920,12 +5931,12 @@ class groupCall final : public GroupCall { int32 unmuted_video_count_; int32 unmuted_video_limit_; int32 version_; - int64 conference_from_call_; - enum Flags : std::int32_t { TITLE_MASK = 8, STREAM_DC_ID_MASK = 16, RECORD_START_DATE_MASK = 32, SCHEDULE_DATE_MASK = 128, UNMUTED_VIDEO_COUNT_MASK = 1024, CONFERENCE_FROM_CALL_MASK = 16384 }; + string invite_link_; + enum Flags : std::int32_t { TITLE_MASK = 8, STREAM_DC_ID_MASK = 16, RECORD_START_DATE_MASK = 32, SCHEDULE_DATE_MASK = 128, UNMUTED_VIDEO_COUNT_MASK = 1024, INVITE_LINK_MASK = 65536 }; groupCall(); - static const std::int32_t ID = -839330845; + static const std::int32_t ID = 1429932961; static object_ptr fetch(TlBufferParser &p); @@ -6334,7 +6345,7 @@ class inputBotInlineMessageMediaAuto final : public InputBotInlineMessage { string message_; array> entities_; object_ptr reply_markup_; - enum Flags : std::int32_t { INVERT_MEDIA_MASK = 8, ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; + enum Flags : std::int32_t { ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; inputBotInlineMessageMediaAuto(int32 flags_, bool invert_media_, string const &message_, array> &&entities_, object_ptr &&reply_markup_); @@ -6359,7 +6370,7 @@ class inputBotInlineMessageText final : public InputBotInlineMessage { string message_; array> entities_; object_ptr reply_markup_; - enum Flags : std::int32_t { NO_WEBPAGE_MASK = 1, INVERT_MEDIA_MASK = 8, ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; + enum Flags : std::int32_t { ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; inputBotInlineMessageText(int32 flags_, bool no_webpage_, bool invert_media_, string const &message_, array> &&entities_, object_ptr &&reply_markup_); @@ -6513,7 +6524,7 @@ class inputBotInlineMessageMediaWebPage final : public InputBotInlineMessage { array> entities_; string url_; object_ptr reply_markup_; - enum Flags : std::int32_t { INVERT_MEDIA_MASK = 8, FORCE_LARGE_MEDIA_MASK = 16, FORCE_SMALL_MEDIA_MASK = 32, OPTIONAL_MASK = 64, ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; + enum Flags : std::int32_t { ENTITIES_MASK = 2, REPLY_MARKUP_MASK = 4 }; inputBotInlineMessageMediaWebPage(int32 flags_, bool invert_media_, bool force_large_media_, bool force_small_media_, bool optional_, string const &message_, array> &&entities_, string const &url_, object_ptr &&reply_markup_); @@ -6707,7 +6718,6 @@ class inputBusinessAwayMessage final : public Object { int32 shortcut_id_; object_ptr schedule_; object_ptr recipients_; - enum Flags : std::int32_t { OFFLINE_ONLY_MASK = 1 }; inputBusinessAwayMessage(int32 flags_, bool offline_only_, int32 shortcut_id_, object_ptr &&schedule_, object_ptr &&recipients_); @@ -6736,7 +6746,7 @@ class inputBusinessBotRecipients final : public Object { bool exclude_selected_; array> users_; array> exclude_users_; - enum Flags : std::int32_t { EXISTING_CHATS_MASK = 1, NEW_CHATS_MASK = 2, CONTACTS_MASK = 4, NON_CONTACTS_MASK = 8, EXCLUDE_SELECTED_MASK = 32, USERS_MASK = 16, EXCLUDE_USERS_MASK = 64 }; + enum Flags : std::int32_t { USERS_MASK = 16, EXCLUDE_USERS_MASK = 64 }; inputBusinessBotRecipients(int32 flags_, bool existing_chats_, bool new_chats_, bool contacts_, bool non_contacts_, bool exclude_selected_, array> &&users_, array> &&exclude_users_); @@ -6837,7 +6847,7 @@ class inputBusinessRecipients final : public Object { bool non_contacts_; bool exclude_selected_; array> users_; - enum Flags : std::int32_t { EXISTING_CHATS_MASK = 1, NEW_CHATS_MASK = 2, CONTACTS_MASK = 4, NON_CONTACTS_MASK = 8, EXCLUDE_SELECTED_MASK = 32, USERS_MASK = 16 }; + enum Flags : std::int32_t { USERS_MASK = 16 }; inputBusinessRecipients(int32 flags_, bool existing_chats_, bool new_chats_, bool contacts_, bool non_contacts_, bool exclude_selected_, array> &&users_); @@ -7410,12 +7420,12 @@ class inputFileStoryDocument final : public InputFile { void store(TlStorerToString &s, const char *field_name) const final; }; +class InputGroupCall; + class InputPeer; class InputStickerSet; -class inputGroupCall; - class InputFileLocation: public Object { public: }; @@ -7431,7 +7441,6 @@ class inputPeerPhotoFileLocationLegacy final : public InputFileLocation { object_ptr peer_; int64 volume_id_; int32 local_id_; - enum Flags : std::int32_t { BIG_MASK = 1 }; inputPeerPhotoFileLocationLegacy(int32 flags_, bool big_, object_ptr &&peer_, int64 volume_id_, int32 local_id_); @@ -7621,7 +7630,6 @@ class inputPeerPhotoFileLocation final : public InputFileLocation { bool big_; object_ptr peer_; int64 photo_id_; - enum Flags : std::int32_t { BIG_MASK = 1 }; inputPeerPhotoFileLocation(int32 flags_, bool big_, object_ptr &&peer_, int64 photo_id_); @@ -7661,14 +7669,14 @@ class inputGroupCallStream final : public InputFileLocation { public: int32 flags_; - object_ptr call_; + object_ptr call_; int64 time_ms_; int32 scale_; int32 video_channel_; int32 video_quality_; enum Flags : std::int32_t { VIDEO_CHANNEL_MASK = 1, VIDEO_QUALITY_MASK = 1 }; - inputGroupCallStream(int32 flags_, object_ptr &&call_, int64 time_ms_, int32 scale_, int32 video_channel_, int32 video_quality_); + inputGroupCallStream(int32 flags_, object_ptr &&call_, int64 time_ms_, int32 scale_, int32 video_channel_, int32 video_quality_); static const std::int32_t ID = 93890858; @@ -7808,7 +7816,13 @@ class inputGeoPoint final : public InputGeoPoint { void store(TlStorerToString &s, const char *field_name) const final; }; -class inputGroupCall final : public Object { +class InputGroupCall: public Object { + public: + + static object_ptr fetch(TlBufferParser &p); +}; + +class inputGroupCall final : public InputGroupCall { std::int32_t get_id() const final { return ID; } @@ -7821,7 +7835,7 @@ class inputGroupCall final : public Object { static const std::int32_t ID = -659913713; - static object_ptr fetch(TlBufferParser &p); + static object_ptr fetch(TlBufferParser &p); explicit inputGroupCall(TlBufferParser &p); @@ -7832,6 +7846,52 @@ class inputGroupCall final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class inputGroupCallSlug final : public InputGroupCall { + std::int32_t get_id() const final { + return ID; + } + + public: + string slug_; + + explicit inputGroupCallSlug(string const &slug_); + + static const std::int32_t ID = -33127873; + + static object_ptr fetch(TlBufferParser &p); + + explicit inputGroupCallSlug(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class inputGroupCallInviteMessage final : public InputGroupCall { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 msg_id_; + + explicit inputGroupCallInviteMessage(int32 msg_id_); + + static const std::int32_t ID = -1945083841; + + static object_ptr fetch(TlBufferParser &p); + + explicit inputGroupCallInviteMessage(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class InputPeer; class InputSavedStarGift; @@ -7957,7 +8017,7 @@ class inputInvoiceStarGift final : public InputInvoice { object_ptr peer_; int64 gift_id_; object_ptr message_; - enum Flags : std::int32_t { HIDE_NAME_MASK = 1, INCLUDE_UPGRADE_MASK = 4, MESSAGE_MASK = 2 }; + enum Flags : std::int32_t { MESSAGE_MASK = 2 }; inputInvoiceStarGift(int32 flags_, bool hide_name_, bool include_upgrade_, object_ptr &&peer_, int64 gift_id_, object_ptr &&message_); @@ -7979,7 +8039,6 @@ class inputInvoiceStarGiftUpgrade final : public InputInvoice { int32 flags_; bool keep_original_details_; object_ptr stargift_; - enum Flags : std::int32_t { KEEP_ORIGINAL_DETAILS_MASK = 1 }; inputInvoiceStarGiftUpgrade(int32 flags_, bool keep_original_details_, object_ptr &&stargift_); @@ -8055,6 +8114,26 @@ class inputInvoiceBusinessBotTransferStars final : public InputInvoice { void store(TlStorerToString &s, const char *field_name) const final; }; +class inputInvoiceStarGiftResale final : public InputInvoice { + std::int32_t get_id() const final { + return ID; + } + + public: + string slug_; + object_ptr to_id_; + + inputInvoiceStarGiftResale(string const &slug_, object_ptr &&to_id_); + + static const std::int32_t ID = 1674298252; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class DocumentAttribute; class InputDocument; @@ -8116,7 +8195,7 @@ class inputMediaUploadedPhoto final : public InputMedia { object_ptr file_; array> stickers_; int32 ttl_seconds_; - enum Flags : std::int32_t { SPOILER_MASK = 4, STICKERS_MASK = 1, TTL_SECONDS_MASK = 2 }; + enum Flags : std::int32_t { STICKERS_MASK = 1, TTL_SECONDS_MASK = 2 }; inputMediaUploadedPhoto(); @@ -8143,7 +8222,7 @@ class inputMediaPhoto final : public InputMedia { bool spoiler_; object_ptr id_; int32 ttl_seconds_; - enum Flags : std::int32_t { SPOILER_MASK = 2, TTL_SECONDS_MASK = 1 }; + enum Flags : std::int32_t { TTL_SECONDS_MASK = 1 }; inputMediaPhoto(); @@ -8227,7 +8306,7 @@ class inputMediaUploadedDocument final : public InputMedia { object_ptr video_cover_; int32 video_timestamp_; int32 ttl_seconds_; - enum Flags : std::int32_t { NOSOUND_VIDEO_MASK = 8, FORCE_FILE_MASK = 16, SPOILER_MASK = 32, THUMB_MASK = 4, STICKERS_MASK = 1, VIDEO_COVER_MASK = 64, VIDEO_TIMESTAMP_MASK = 128, TTL_SECONDS_MASK = 2 }; + enum Flags : std::int32_t { THUMB_MASK = 4, STICKERS_MASK = 1, VIDEO_COVER_MASK = 64, VIDEO_TIMESTAMP_MASK = 128, TTL_SECONDS_MASK = 2 }; inputMediaUploadedDocument(); @@ -8257,7 +8336,7 @@ class inputMediaDocument final : public InputMedia { int32 video_timestamp_; int32 ttl_seconds_; string query_; - enum Flags : std::int32_t { SPOILER_MASK = 4, VIDEO_COVER_MASK = 8, VIDEO_TIMESTAMP_MASK = 16, TTL_SECONDS_MASK = 1, QUERY_MASK = 2 }; + enum Flags : std::int32_t { VIDEO_COVER_MASK = 8, VIDEO_TIMESTAMP_MASK = 16, TTL_SECONDS_MASK = 1, QUERY_MASK = 2 }; inputMediaDocument(); @@ -8312,7 +8391,7 @@ class inputMediaPhotoExternal final : public InputMedia { bool spoiler_; string url_; int32 ttl_seconds_; - enum Flags : std::int32_t { SPOILER_MASK = 2, TTL_SECONDS_MASK = 1 }; + enum Flags : std::int32_t { TTL_SECONDS_MASK = 1 }; inputMediaPhotoExternal(); @@ -8341,7 +8420,7 @@ class inputMediaDocumentExternal final : public InputMedia { int32 ttl_seconds_; object_ptr video_cover_; int32 video_timestamp_; - enum Flags : std::int32_t { SPOILER_MASK = 2, TTL_SECONDS_MASK = 1, VIDEO_COVER_MASK = 4, VIDEO_TIMESTAMP_MASK = 8 }; + enum Flags : std::int32_t { TTL_SECONDS_MASK = 1, VIDEO_COVER_MASK = 4, VIDEO_TIMESTAMP_MASK = 8 }; inputMediaDocumentExternal(); @@ -8426,7 +8505,7 @@ class inputMediaGeoLive final : public InputMedia { int32 heading_; int32 period_; int32 proximity_notification_radius_; - enum Flags : std::int32_t { STOPPED_MASK = 1, HEADING_MASK = 4, PERIOD_MASK = 2, PROXIMITY_NOTIFICATION_RADIUS_MASK = 8 }; + enum Flags : std::int32_t { HEADING_MASK = 4, PERIOD_MASK = 2, PROXIMITY_NOTIFICATION_RADIUS_MASK = 8 }; inputMediaGeoLive(); @@ -8529,7 +8608,6 @@ class inputMediaWebPage final : public InputMedia { bool force_small_media_; bool optional_; string url_; - enum Flags : std::int32_t { FORCE_LARGE_MEDIA_MASK = 1, FORCE_SMALL_MEDIA_MASK = 2, OPTIONAL_MASK = 4 }; inputMediaWebPage(); @@ -8779,7 +8857,6 @@ class inputPaymentCredentials final : public InputPaymentCredentials { int32 flags_; bool save_; object_ptr data_; - enum Flags : std::int32_t { SAVE_MASK = 1 }; inputPaymentCredentials(int32 flags_, bool save_, object_ptr &&data_); @@ -9666,6 +9743,25 @@ class inputSavedStarGiftChat final : public InputSavedStarGift { void store(TlStorerToString &s, const char *field_name) const final; }; +class inputSavedStarGiftSlug final : public InputSavedStarGift { + std::int32_t get_id() const final { + return ID; + } + + public: + string slug_; + + explicit inputSavedStarGiftSlug(string const &slug_); + + static const std::int32_t ID = 545636920; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class InputSecureFile: public Object { public: }; @@ -9786,7 +9882,6 @@ class inputStarsTransaction final : public Object { int32 flags_; bool refund_; string id_; - enum Flags : std::int32_t { REFUND_MASK = 1 }; inputStarsTransaction(int32 flags_, bool refund_, string const &id_); @@ -10112,7 +10207,6 @@ class inputStorePaymentPremiumSubscription final : public InputStorePaymentPurpo int32 flags_; bool restore_; bool upgrade_; - enum Flags : std::int32_t { RESTORE_MASK = 1, UPGRADE_MASK = 2 }; inputStorePaymentPremiumSubscription(int32 flags_, bool restore_, bool upgrade_); @@ -10188,7 +10282,7 @@ class inputStorePaymentPremiumGiveaway final : public InputStorePaymentPurpose { int32 until_date_; string currency_; int64 amount_; - enum Flags : std::int32_t { ONLY_NEW_SUBSCRIBERS_MASK = 1, WINNERS_ARE_VISIBLE_MASK = 8, ADDITIONAL_PEERS_MASK = 2, COUNTRIES_ISO2_MASK = 4, PRIZE_DESCRIPTION_MASK = 16 }; + enum Flags : std::int32_t { ADDITIONAL_PEERS_MASK = 2, COUNTRIES_ISO2_MASK = 4, PRIZE_DESCRIPTION_MASK = 16 }; inputStorePaymentPremiumGiveaway(int32 flags_, bool only_new_subscribers_, bool winners_are_visible_, object_ptr &&boost_peer_, array> &&additional_peers_, array &&countries_iso2_, string const &prize_description_, int64 random_id_, int32 until_date_, string const ¤cy_, int64 amount_); @@ -10263,7 +10357,7 @@ class inputStorePaymentStarsGiveaway final : public InputStorePaymentPurpose { string currency_; int64 amount_; int32 users_; - enum Flags : std::int32_t { ONLY_NEW_SUBSCRIBERS_MASK = 1, WINNERS_ARE_VISIBLE_MASK = 8, ADDITIONAL_PEERS_MASK = 2, COUNTRIES_ISO2_MASK = 4, PRIZE_DESCRIPTION_MASK = 16 }; + enum Flags : std::int32_t { ADDITIONAL_PEERS_MASK = 2, COUNTRIES_ISO2_MASK = 4, PRIZE_DESCRIPTION_MASK = 16 }; inputStorePaymentStarsGiveaway(int32 flags_, bool only_new_subscribers_, bool winners_are_visible_, int64 stars_, object_ptr &&boost_peer_, array> &&additional_peers_, array &&countries_iso2_, string const &prize_description_, int64 random_id_, int32 until_date_, string const ¤cy_, int64 amount_, int32 users_); @@ -10288,7 +10382,6 @@ class inputStorePaymentAuthCode final : public InputStorePaymentPurpose { string phone_code_hash_; string currency_; int64 amount_; - enum Flags : std::int32_t { RESTORE_MASK = 1 }; inputStorePaymentAuthCode(int32 flags_, bool restore_, string const &phone_number_, string const &phone_code_hash_, string const ¤cy_, int64 amount_); @@ -10364,7 +10457,7 @@ class inputThemeSettings final : public Object { array message_colors_; object_ptr wallpaper_; object_ptr wallpaper_settings_; - enum Flags : std::int32_t { MESSAGE_COLORS_ANIMATED_MASK = 4, OUTBOX_ACCENT_COLOR_MASK = 8, MESSAGE_COLORS_MASK = 1, WALLPAPER_MASK = 2, WALLPAPER_SETTINGS_MASK = 2 }; + enum Flags : std::int32_t { OUTBOX_ACCENT_COLOR_MASK = 8, MESSAGE_COLORS_MASK = 1, WALLPAPER_MASK = 2, WALLPAPER_SETTINGS_MASK = 2 }; inputThemeSettings(int32 flags_, bool message_colors_animated_, object_ptr &&base_theme_, int32 accent_color_, int32 outbox_accent_color_, array &&message_colors_, object_ptr &&wallpaper_, object_ptr &&wallpaper_settings_); @@ -10623,7 +10716,7 @@ class inputWebFileAudioAlbumThumbLocation final : public InputWebFileLocation { object_ptr document_; string title_; string performer_; - enum Flags : std::int32_t { SMALL_MASK = 4, DOCUMENT_MASK = 1, TITLE_MASK = 2, PERFORMER_MASK = 2 }; + enum Flags : std::int32_t { DOCUMENT_MASK = 1, TITLE_MASK = 2, PERFORMER_MASK = 2 }; inputWebFileAudioAlbumThumbLocation(int32 flags_, bool small_, object_ptr &&document_, string const &title_, string const &performer_); @@ -10660,7 +10753,7 @@ class invoice final : public Object { array suggested_tip_amounts_; string terms_url_; int32 subscription_period_; - enum Flags : std::int32_t { TEST_MASK = 1, NAME_REQUESTED_MASK = 2, PHONE_REQUESTED_MASK = 4, EMAIL_REQUESTED_MASK = 8, SHIPPING_ADDRESS_REQUESTED_MASK = 16, FLEXIBLE_MASK = 32, PHONE_TO_PROVIDER_MASK = 64, EMAIL_TO_PROVIDER_MASK = 128, RECURRING_MASK = 512, MAX_TIP_AMOUNT_MASK = 256, SUGGESTED_TIP_AMOUNTS_MASK = 256, TERMS_URL_MASK = 1024, SUBSCRIPTION_PERIOD_MASK = 2048 }; + enum Flags : std::int32_t { MAX_TIP_AMOUNT_MASK = 256, SUGGESTED_TIP_AMOUNTS_MASK = 256, TERMS_URL_MASK = 1024, SUBSCRIPTION_PERIOD_MASK = 2048 }; invoice(); @@ -10958,7 +11051,6 @@ class keyboardButtonCallback final : public KeyboardButton { bool requires_password_; string text_; bytes data_; - enum Flags : std::int32_t { REQUIRES_PASSWORD_MASK = 1 }; keyboardButtonCallback(); @@ -11032,7 +11124,7 @@ class keyboardButtonSwitchInline final : public KeyboardButton { string text_; string query_; array> peer_types_; - enum Flags : std::int32_t { SAME_PEER_MASK = 1, PEER_TYPES_MASK = 2 }; + enum Flags : std::int32_t { PEER_TYPES_MASK = 2 }; keyboardButtonSwitchInline(); @@ -11135,7 +11227,7 @@ class inputKeyboardButtonUrlAuth final : public KeyboardButton { string fwd_text_; string url_; object_ptr bot_; - enum Flags : std::int32_t { REQUEST_WRITE_ACCESS_MASK = 1, FWD_TEXT_MASK = 2 }; + enum Flags : std::int32_t { FWD_TEXT_MASK = 2 }; inputKeyboardButtonUrlAuth(); @@ -11314,7 +11406,6 @@ class inputKeyboardButtonRequestPeer final : public KeyboardButton { int32 button_id_; object_ptr peer_type_; int32 max_quantity_; - enum Flags : std::int32_t { NAME_REQUESTED_MASK = 1, USERNAME_REQUESTED_MASK = 2, PHOTO_REQUESTED_MASK = 4 }; inputKeyboardButtonRequestPeer(); @@ -11661,7 +11752,6 @@ class mediaAreaSuggestedReaction final : public MediaArea { bool flipped_; object_ptr coordinates_; object_ptr reaction_; - enum Flags : std::int32_t { DARK_MASK = 1, FLIPPED_MASK = 2 }; mediaAreaSuggestedReaction(); @@ -11976,6 +12066,8 @@ class messageService final : public Message { class BotApp; +class InputGroupCall; + class Peer; class PhoneCallDiscardReason; @@ -11990,8 +12082,6 @@ class StarGift; class WallPaper; -class inputGroupCall; - class paymentCharge; class paymentRequestedInfo; @@ -12438,7 +12528,7 @@ class messageActionGroupCall final : public MessageAction { public: int32 flags_; - object_ptr call_; + object_ptr call_; int32 duration_; enum Flags : std::int32_t { DURATION_MASK = 1 }; @@ -12457,7 +12547,7 @@ class messageActionInviteToGroupCall final : public MessageAction { } public: - object_ptr call_; + object_ptr call_; array users_; static const std::int32_t ID = 1345295095; @@ -12495,7 +12585,7 @@ class messageActionGroupCallScheduled final : public MessageAction { } public: - object_ptr call_; + object_ptr call_; int32 schedule_date_; static const std::int32_t ID = -1281329567; @@ -12915,11 +13005,14 @@ class messageActionStarGiftUnique final : public MessageAction { object_ptr from_id_; object_ptr peer_; int64 saved_id_; - enum Flags : std::int32_t { CAN_EXPORT_AT_MASK = 8, TRANSFER_STARS_MASK = 16, SAVED_ID_MASK = 128 }; + int64 resale_stars_; + int32 can_transfer_at_; + int32 can_resell_at_; + enum Flags : std::int32_t { CAN_EXPORT_AT_MASK = 8, TRANSFER_STARS_MASK = 16, SAVED_ID_MASK = 128, RESALE_STARS_MASK = 256, CAN_TRANSFER_AT_MASK = 512, CAN_RESELL_AT_MASK = 1024 }; messageActionStarGiftUnique(); - static const std::int32_t ID = -1394619519; + static const std::int32_t ID = 775611918; static object_ptr fetch(TlBufferParser &p); @@ -12961,6 +13054,30 @@ class messageActionPaidMessagesPrice final : public MessageAction { void store(TlStorerToString &s, const char *field_name) const final; }; +class messageActionConferenceCall final : public MessageAction { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 flags_; + bool missed_; + bool active_; + bool video_; + int64 call_id_; + int32 duration_; + array> other_participants_; + enum Flags : std::int32_t { DURATION_MASK = 4 }; + + messageActionConferenceCall(); + + static const std::int32_t ID = 805187450; + + static object_ptr fetch(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class InputUser; class MessageEntity: public Object { @@ -13464,7 +13581,6 @@ class messageEntityBlockquote final : public MessageEntity { bool collapsed_; int32 offset_; int32 length_; - enum Flags : std::int32_t { COLLAPSED_MASK = 1 }; messageEntityBlockquote(); @@ -14440,7 +14556,6 @@ class inputMessagesFilterPhoneCalls final : public MessagesFilter { public: int32 flags_; bool missed_; - enum Flags : std::int32_t { MISSED_MASK = 1 }; inputMessagesFilterPhoneCalls(); @@ -16029,14 +16144,34 @@ class peerStories final : public Object { void store(TlStorerToString &s, const char *field_name) const final; }; +class textWithEntities; + +class pendingSuggestion final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + string suggestion_; + object_ptr title_; + object_ptr description_; + string url_; + + static const std::int32_t ID = -404214254; + + static object_ptr fetch(TlBufferParser &p); + + explicit pendingSuggestion(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class PhoneCallDiscardReason; class PhoneConnection; class dataJSON; -class inputGroupCall; - class phoneCallProtocol; class PhoneCall: public Object { @@ -16077,12 +16212,11 @@ class phoneCallWaiting final : public PhoneCall { int64 participant_id_; object_ptr protocol_; int32 receive_date_; - object_ptr conference_call_; enum Flags : std::int32_t { RECEIVE_DATE_MASK = 1 }; phoneCallWaiting(); - static const std::int32_t ID = -288085928; + static const std::int32_t ID = -987599081; static object_ptr fetch(TlBufferParser &p); @@ -16104,11 +16238,10 @@ class phoneCallRequested final : public PhoneCall { int64 participant_id_; bytes g_a_hash_; object_ptr protocol_; - object_ptr conference_call_; phoneCallRequested(); - static const std::int32_t ID = 1161174115; + static const std::int32_t ID = 347139340; static object_ptr fetch(TlBufferParser &p); @@ -16130,11 +16263,10 @@ class phoneCallAccepted final : public PhoneCall { int64 participant_id_; bytes g_b_; object_ptr protocol_; - object_ptr conference_call_; phoneCallAccepted(); - static const std::int32_t ID = 587035009; + static const std::int32_t ID = 912311057; static object_ptr fetch(TlBufferParser &p); @@ -16150,6 +16282,7 @@ class phoneCall final : public PhoneCall { int32 flags_; bool p2p_allowed_; bool video_; + bool conference_supported_; int64 id_; int64 access_hash_; int32 date_; @@ -16161,11 +16294,10 @@ class phoneCall final : public PhoneCall { array> connections_; int32 start_date_; object_ptr custom_parameters_; - object_ptr conference_call_; phoneCall(); - static const std::int32_t ID = 1000707084; + static const std::int32_t ID = 810769141; static object_ptr fetch(TlBufferParser &p); @@ -16185,12 +16317,11 @@ class phoneCallDiscarded final : public PhoneCall { int64 id_; object_ptr reason_; int32 duration_; - object_ptr conference_call_; enum Flags : std::int32_t { DURATION_MASK = 2 }; phoneCallDiscarded(); - static const std::int32_t ID = -103656189; + static const std::int32_t ID = 1355435489; static object_ptr fetch(TlBufferParser &p); @@ -16275,6 +16406,29 @@ class phoneCallDiscardReasonBusy final : public PhoneCallDiscardReason { void store(TlStorerToString &s, const char *field_name) const final; }; +class phoneCallDiscardReasonMigrateConferenceCall final : public PhoneCallDiscardReason { + std::int32_t get_id() const final { + return ID; + } + + public: + string slug_; + + explicit phoneCallDiscardReasonMigrateConferenceCall(string const &slug_); + + static const std::int32_t ID = -1615072777; + + static object_ptr fetch(TlBufferParser &p); + + explicit phoneCallDiscardReasonMigrateConferenceCall(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class phoneCallProtocol final : public Object { std::int32_t get_id() const final { return ID; @@ -16287,7 +16441,6 @@ class phoneCallProtocol final : public Object { int32 min_layer_; int32 max_layer_; array library_versions_; - enum Flags : std::int32_t { UDP_P2P_MASK = 1, UDP_REFLECTOR_MASK = 2 }; phoneCallProtocol(); @@ -16555,7 +16708,7 @@ class poll final : public Object { array> answers_; int32 close_period_; int32 close_date_; - enum Flags : std::int32_t { CLOSED_MASK = 1, PUBLIC_VOTERS_MASK = 2, MULTIPLE_CHOICE_MASK = 4, QUIZ_MASK = 8, CLOSE_PERIOD_MASK = 16, CLOSE_DATE_MASK = 32 }; + enum Flags : std::int32_t { CLOSE_PERIOD_MASK = 16, CLOSE_DATE_MASK = 32 }; poll(); @@ -17621,7 +17774,6 @@ class replyKeyboardHide final : public ReplyMarkup { public: int32 flags_; bool selective_; - enum Flags : std::int32_t { SELECTIVE_MASK = 4 }; replyKeyboardHide(); @@ -17648,7 +17800,7 @@ class replyKeyboardForceReply final : public ReplyMarkup { bool single_use_; bool selective_; string placeholder_; - enum Flags : std::int32_t { SINGLE_USE_MASK = 2, SELECTIVE_MASK = 4, PLACEHOLDER_MASK = 8 }; + enum Flags : std::int32_t { PLACEHOLDER_MASK = 8 }; replyKeyboardForceReply(); @@ -17678,7 +17830,7 @@ class replyKeyboardMarkup final : public ReplyMarkup { bool persistent_; array> rows_; string placeholder_; - enum Flags : std::int32_t { RESIZE_MASK = 1, SINGLE_USE_MASK = 2, SELECTIVE_MASK = 4, PERSISTENT_MASK = 16, PLACEHOLDER_MASK = 8 }; + enum Flags : std::int32_t { PLACEHOLDER_MASK = 8 }; replyKeyboardMarkup(); @@ -17988,7 +18140,7 @@ class requestPeerTypeChat final : public RequestPeerType { bool forum_; object_ptr user_admin_rights_; object_ptr bot_admin_rights_; - enum Flags : std::int32_t { CREATOR_MASK = 1, BOT_PARTICIPANT_MASK = 32, HAS_USERNAME_MASK = 8, FORUM_MASK = 16, USER_ADMIN_RIGHTS_MASK = 2, BOT_ADMIN_RIGHTS_MASK = 4 }; + enum Flags : std::int32_t { HAS_USERNAME_MASK = 8, FORUM_MASK = 16, USER_ADMIN_RIGHTS_MASK = 2, BOT_ADMIN_RIGHTS_MASK = 4 }; requestPeerTypeChat(); @@ -18016,7 +18168,7 @@ class requestPeerTypeBroadcast final : public RequestPeerType { bool has_username_; object_ptr user_admin_rights_; object_ptr bot_admin_rights_; - enum Flags : std::int32_t { CREATOR_MASK = 1, HAS_USERNAME_MASK = 8, USER_ADMIN_RIGHTS_MASK = 2, BOT_ADMIN_RIGHTS_MASK = 4 }; + enum Flags : std::int32_t { HAS_USERNAME_MASK = 8, USER_ADMIN_RIGHTS_MASK = 2, BOT_ADMIN_RIGHTS_MASK = 4 }; requestPeerTypeBroadcast(); @@ -18554,11 +18706,13 @@ class savedStarGift final : public Object { int64 upgrade_stars_; int32 can_export_at_; int64 transfer_stars_; - enum Flags : std::int32_t { MSG_ID_MASK = 8, SAVED_ID_MASK = 2048, CONVERT_STARS_MASK = 16, UPGRADE_STARS_MASK = 64, CAN_EXPORT_AT_MASK = 128, TRANSFER_STARS_MASK = 256 }; + int32 can_transfer_at_; + int32 can_resell_at_; + enum Flags : std::int32_t { MSG_ID_MASK = 8, SAVED_ID_MASK = 2048, CONVERT_STARS_MASK = 16, UPGRADE_STARS_MASK = 64, CAN_EXPORT_AT_MASK = 128, TRANSFER_STARS_MASK = 256, CAN_TRANSFER_AT_MASK = 8192, CAN_RESELL_AT_MASK = 16384 }; savedStarGift(); - static const std::int32_t ID = 1616305061; + static const std::int32_t ID = -539360103; static object_ptr fetch(TlBufferParser &p); @@ -19973,15 +20127,18 @@ class starGift final : public StarGift { int64 stars_; int32 availability_remains_; int32 availability_total_; + int64 availability_resale_; int64 convert_stars_; int32 first_sale_date_; int32 last_sale_date_; int64 upgrade_stars_; - enum Flags : std::int32_t { AVAILABILITY_REMAINS_MASK = 1, AVAILABILITY_TOTAL_MASK = 1, FIRST_SALE_DATE_MASK = 2, LAST_SALE_DATE_MASK = 2, UPGRADE_STARS_MASK = 8 }; + int64 resell_min_stars_; + string title_; + enum Flags : std::int32_t { AVAILABILITY_REMAINS_MASK = 1, AVAILABILITY_TOTAL_MASK = 1, AVAILABILITY_RESALE_MASK = 16, FIRST_SALE_DATE_MASK = 2, LAST_SALE_DATE_MASK = 2, UPGRADE_STARS_MASK = 8, RESELL_MIN_STARS_MASK = 16, TITLE_MASK = 32 }; starGift(); - static const std::int32_t ID = 46953416; + static const std::int32_t ID = -970274264; static object_ptr fetch(TlBufferParser &p); @@ -20006,11 +20163,12 @@ class starGiftUnique final : public StarGift { int32 availability_issued_; int32 availability_total_; string gift_address_; - enum Flags : std::int32_t { OWNER_NAME_MASK = 2, OWNER_ADDRESS_MASK = 4, GIFT_ADDRESS_MASK = 8 }; + int64 resell_stars_; + enum Flags : std::int32_t { OWNER_NAME_MASK = 2, OWNER_ADDRESS_MASK = 4, GIFT_ADDRESS_MASK = 8, RESELL_STARS_MASK = 16 }; starGiftUnique(); - static const std::int32_t ID = 1549979985; + static const std::int32_t ID = 1678891913; static object_ptr fetch(TlBufferParser &p); @@ -20074,13 +20232,14 @@ class starGiftAttributeBackdrop final : public StarGiftAttribute { public: string name_; + int32 backdrop_id_; int32 center_color_; int32 edge_color_; int32 pattern_color_; int32 text_color_; int32 rarity_permille_; - static const std::int32_t ID = -1809377438; + static const std::int32_t ID = -650279524; static object_ptr fetch(TlBufferParser &p); @@ -20110,6 +20269,101 @@ class starGiftAttributeOriginalDetails final : public StarGiftAttribute { void store(TlStorerToString &s, const char *field_name) const final; }; +class StarGiftAttributeId; + +class starGiftAttributeCounter final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr attribute_; + int32 count_; + + static const std::int32_t ID = 783398488; + + static object_ptr fetch(TlBufferParser &p); + + explicit starGiftAttributeCounter(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class StarGiftAttributeId: public Object { + public: + + static object_ptr fetch(TlBufferParser &p); +}; + +class starGiftAttributeIdModel final : public StarGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int64 document_id_; + + explicit starGiftAttributeIdModel(int64 document_id_); + + static const std::int32_t ID = 1219145276; + + static object_ptr fetch(TlBufferParser &p); + + explicit starGiftAttributeIdModel(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class starGiftAttributeIdPattern final : public StarGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int64 document_id_; + + explicit starGiftAttributeIdPattern(int64 document_id_); + + static const std::int32_t ID = 1242965043; + + static object_ptr fetch(TlBufferParser &p); + + explicit starGiftAttributeIdPattern(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + +class starGiftAttributeIdBackdrop final : public StarGiftAttributeId { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 backdrop_id_; + + explicit starGiftAttributeIdBackdrop(int32 backdrop_id_); + + static const std::int32_t ID = 520210263; + + static object_ptr fetch(TlBufferParser &p); + + explicit starGiftAttributeIdBackdrop(TlBufferParser &p); + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class starsAmount; class starRefProgram final : public Object { @@ -20358,6 +20612,7 @@ class starsTransaction final : public Object { bool reaction_; bool stargift_upgrade_; bool business_transfer_; + bool stargift_resale_; string id_; object_ptr stars_; int32 date_; @@ -21521,6 +21776,8 @@ class InlineQueryPeerType; class InputBotInlineMessageID; +class InputGroupCall; + class Message; class MessageEntity; @@ -21575,8 +21832,6 @@ class folderPeer; class groupCallParticipant; -class inputGroupCall; - class langPackDifference; class messageReactions; @@ -23240,7 +23495,7 @@ class updateGroupCallParticipants final : public Update { } public: - object_ptr call_; + object_ptr call_; array> participants_; int32 version_; @@ -24294,6 +24549,26 @@ class updateSentPhoneCode final : public Update { void store(TlStorerToString &s, const char *field_name) const final; }; +class updateGroupCallChainBlocks final : public Update { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr call_; + int32 sub_chain_id_; + array blocks_; + int32 next_offset_; + + static const std::int32_t ID = -1535694705; + + static object_ptr fetch(TlBufferParser &p); + + explicit updateGroupCallChainBlocks(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class Chat; class MessageEntity; @@ -25075,7 +25350,7 @@ class wallPaperSettings final : public Object { int32 intensity_; int32 rotation_; string emoticon_; - enum Flags : std::int32_t { BLUR_MASK = 2, MOTION_MASK = 4, BACKGROUND_COLOR_MASK = 1, SECOND_BACKGROUND_COLOR_MASK = 16, THIRD_BACKGROUND_COLOR_MASK = 32, FOURTH_BACKGROUND_COLOR_MASK = 64, INTENSITY_MASK = 8, ROTATION_MASK = 16, EMOTICON_MASK = 128 }; + enum Flags : std::int32_t { BACKGROUND_COLOR_MASK = 1, SECOND_BACKGROUND_COLOR_MASK = 16, THIRD_BACKGROUND_COLOR_MASK = 32, FOURTH_BACKGROUND_COLOR_MASK = 64, INTENSITY_MASK = 8, ROTATION_MASK = 16, EMOTICON_MASK = 128 }; wallPaperSettings(); @@ -27762,6 +28037,8 @@ class Peer; class User; +class pendingSuggestion; + class help_PromoData: public Object { public: @@ -27795,15 +28072,18 @@ class help_promoData final : public help_PromoData { bool proxy_; int32 expires_; object_ptr peer_; - array> chats_; - array> users_; string psa_type_; string psa_message_; + array pending_suggestions_; + array dismissed_suggestions_; + object_ptr custom_pending_suggestion_; + array> chats_; + array> users_; enum Flags : std::int32_t { PSA_TYPE_MASK = 2, PSA_MESSAGE_MASK = 4 }; help_promoData(); - static const std::int32_t ID = -1942390465; + static const std::int32_t ID = 145021050; static object_ptr fetch(TlBufferParser &p); @@ -30382,6 +30662,42 @@ class payments_paymentVerificationNeeded final : public payments_PaymentResult { void store(TlStorerToString &s, const char *field_name) const final; }; +class Chat; + +class StarGift; + +class StarGiftAttribute; + +class User; + +class starGiftAttributeCounter; + +class payments_resaleStarGifts final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 flags_; + int32 count_; + array> gifts_; + string next_offset_; + array> attributes_; + int64 attributes_hash_; + array> chats_; + array> counters_; + array> users_; + enum Flags : std::int32_t { NEXT_OFFSET_MASK = 1, ATTRIBUTES_HASH_MASK = 2 }; + + payments_resaleStarGifts(); + + static const std::int32_t ID = -1803939105; + + static object_ptr fetch(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class paymentRequestedInfo; class payments_savedInfo final : public Object { @@ -31473,6 +31789,23 @@ class stories_allStories final : public stories_AllStories { void store(TlStorerToString &s, const char *field_name) const final; }; +class stories_canSendStoryCount final : public Object { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 count_remains_; + + static const std::int32_t ID = -1014513586; + + static object_ptr fetch(TlBufferParser &p); + + explicit stories_canSendStoryCount(TlBufferParser &p); + + void store(TlStorerToString &s, const char *field_name) const final; +}; + class Chat; class User; @@ -32067,7 +32400,6 @@ class account_addNoPaidMessagesException final : public Function { int32 flags_; bool refund_charged_; object_ptr user_id_; - enum Flags : std::int32_t { REFUND_CHARGED_MASK = 1 }; mutable int32 var0; account_addNoPaidMessagesException(int32 flags_, bool refund_charged_, object_ptr &&user_id_); @@ -32116,7 +32448,7 @@ class account_changeAuthorizationSettings final : public Function { int64 hash_; bool encrypted_requests_disabled_; bool call_requests_disabled_; - enum Flags : std::int32_t { CONFIRMED_MASK = 8, ENCRYPTED_REQUESTS_DISABLED_MASK = 1, CALL_REQUESTS_DISABLED_MASK = 2 }; + enum Flags : std::int32_t { ENCRYPTED_REQUESTS_DISABLED_MASK = 1, CALL_REQUESTS_DISABLED_MASK = 2 }; mutable int32 var0; account_changeAuthorizationSettings(int32 flags_, bool confirmed_, int64 hash_, bool encrypted_requests_disabled_, bool call_requests_disabled_); @@ -32491,7 +32823,6 @@ class account_finishTakeoutSession final : public Function { public: int32 flags_; bool success_; - enum Flags : std::int32_t { SUCCESS_MASK = 1 }; mutable int32 var0; account_finishTakeoutSession(int32 flags_, bool success_); @@ -33020,7 +33351,7 @@ class account_getNotifyExceptions final : public Function { bool compare_sound_; bool compare_stories_; object_ptr peer_; - enum Flags : std::int32_t { COMPARE_SOUND_MASK = 2, COMPARE_STORIES_MASK = 4, PEER_MASK = 1 }; + enum Flags : std::int32_t { PEER_MASK = 1 }; mutable int32 var0; account_getNotifyExceptions(int32 flags_, bool compare_sound_, bool compare_stories_, object_ptr &&peer_); @@ -33439,7 +33770,7 @@ class account_initTakeoutSession final : public Function { bool message_channels_; bool files_; int64 file_max_size_; - enum Flags : std::int32_t { CONTACTS_MASK = 1, MESSAGE_USERS_MASK = 2, MESSAGE_CHATS_MASK = 4, MESSAGE_MEGAGROUPS_MASK = 8, MESSAGE_CHANNELS_MASK = 16, FILES_MASK = 32, FILE_MAX_SIZE_MASK = 32 }; + enum Flags : std::int32_t { FILE_MAX_SIZE_MASK = 32 }; mutable int32 var0; account_initTakeoutSession(int32 flags_, bool contacts_, bool message_users_, bool message_chats_, bool message_megagroups_, bool message_channels_, bool files_, int64 file_max_size_); @@ -33472,7 +33803,7 @@ class account_installTheme final : public Function { object_ptr theme_; string format_; object_ptr base_theme_; - enum Flags : std::int32_t { DARK_MASK = 1, THEME_MASK = 2, FORMAT_MASK = 4, BASE_THEME_MASK = 8 }; + enum Flags : std::int32_t { THEME_MASK = 2, FORMAT_MASK = 4, BASE_THEME_MASK = 8 }; mutable int32 var0; account_installTheme(int32 flags_, bool dark_, object_ptr &&theme_, string const &format_, object_ptr &&base_theme_); @@ -33554,7 +33885,6 @@ class account_registerDevice final : public Function { bool app_sandbox_; bytes secret_; array other_uids_; - enum Flags : std::int32_t { NO_MUTED_MASK = 1 }; mutable int32 var0; account_registerDevice(int32 flags_, bool no_muted_, int32 token_type_, string const &token_, bool app_sandbox_, bytes &&secret_, array &&other_uids_); @@ -33841,7 +34171,6 @@ class account_saveAutoDownloadSettings final : public Function { bool low_; bool high_; object_ptr settings_; - enum Flags : std::int32_t { LOW_MASK = 1, HIGH_MASK = 2 }; mutable int32 var0; account_saveAutoDownloadSettings(int32 flags_, bool low_, bool high_, object_ptr &&settings_); @@ -33875,7 +34204,7 @@ class account_saveAutoSaveSettings final : public Function { bool broadcasts_; object_ptr peer_; object_ptr settings_; - enum Flags : std::int32_t { USERS_MASK = 1, CHATS_MASK = 2, BROADCASTS_MASK = 4, PEER_MASK = 8 }; + enum Flags : std::int32_t { PEER_MASK = 8 }; mutable int32 var0; account_saveAutoSaveSettings(int32 flags_, bool users_, bool chats_, bool broadcasts_, object_ptr &&peer_, object_ptr &&settings_); @@ -34195,7 +34524,6 @@ class account_setContentSettings final : public Function { public: int32 flags_; bool sensitive_enabled_; - enum Flags : std::int32_t { SENSITIVE_ENABLED_MASK = 1 }; mutable int32 var0; account_setContentSettings(int32 flags_, bool sensitive_enabled_); @@ -34570,7 +34898,7 @@ class account_updateColor final : public Function { bool for_profile_; int32 color_; int64 background_emoji_id_; - enum Flags : std::int32_t { FOR_PROFILE_MASK = 2, COLOR_MASK = 4, BACKGROUND_EMOJI_ID_MASK = 1 }; + enum Flags : std::int32_t { COLOR_MASK = 4, BACKGROUND_EMOJI_ID_MASK = 1 }; mutable int32 var0; account_updateColor(int32 flags_, bool for_profile_, int32 color_, int64 background_emoji_id_); @@ -34607,7 +34935,7 @@ class account_updateConnectedBot final : public Function { object_ptr rights_; object_ptr bot_; object_ptr recipients_; - enum Flags : std::int32_t { DELETED_MASK = 2, RIGHTS_MASK = 1 }; + enum Flags : std::int32_t { RIGHTS_MASK = 1 }; mutable int32 var0; account_updateConnectedBot(int32 flags_, bool deleted_, object_ptr &&rights_, object_ptr &&bot_, object_ptr &&recipients_); @@ -34950,7 +35278,6 @@ class account_uploadWallPaper final : public Function { object_ptr file_; string mime_type_; object_ptr settings_; - enum Flags : std::int32_t { FOR_CHAT_MASK = 1 }; mutable int32 var0; account_uploadWallPaper(int32 flags_, bool for_chat_, object_ptr &&file_, string const &mime_type_, object_ptr &&settings_); @@ -35612,7 +35939,6 @@ class auth_signUp final : public Function { string phone_code_hash_; string first_name_; string last_name_; - enum Flags : std::int32_t { NO_JOINED_NOTIFICATIONS_MASK = 1 }; mutable int32 var0; auth_signUp(int32 flags_, bool no_joined_notifications_, string const &phone_number_, string const &phone_code_hash_, string const &first_name_, string const &last_name_); @@ -36333,7 +36659,7 @@ class bots_setCustomVerification final : public Function { object_ptr bot_; object_ptr peer_; string custom_description_; - enum Flags : std::int32_t { ENABLED_MASK = 2, BOT_MASK = 1, CUSTOM_DESCRIPTION_MASK = 4 }; + enum Flags : std::int32_t { BOT_MASK = 1, CUSTOM_DESCRIPTION_MASK = 4 }; mutable int32 var0; bots_setCustomVerification(int32 flags_, bool enabled_, object_ptr &&bot_, object_ptr &&peer_, string const &custom_description_); @@ -36537,7 +36863,7 @@ class channels_createChannel final : public Function { object_ptr geo_point_; string address_; int32 ttl_period_; - enum Flags : std::int32_t { BROADCAST_MASK = 1, MEGAGROUP_MASK = 2, FOR_IMPORT_MASK = 8, FORUM_MASK = 32, GEO_POINT_MASK = 4, ADDRESS_MASK = 4, TTL_PERIOD_MASK = 16 }; + enum Flags : std::int32_t { GEO_POINT_MASK = 4, ADDRESS_MASK = 4, TTL_PERIOD_MASK = 16 }; mutable int32 var0; channels_createChannel(int32 flags_, bool broadcast_, bool megagroup_, bool for_import_, bool forum_, string const &title_, string const &about_, object_ptr &&geo_point_, string const &address_, int32 ttl_period_); @@ -36658,7 +36984,6 @@ class channels_deleteHistory final : public Function { bool for_everyone_; object_ptr channel_; int32 max_id_; - enum Flags : std::int32_t { FOR_EVERYONE_MASK = 1 }; mutable int32 var0; channels_deleteHistory(int32 flags_, bool for_everyone_, object_ptr &&channel_, int32 max_id_); @@ -36999,7 +37324,6 @@ class channels_exportMessageLink final : public Function { bool thread_; object_ptr channel_; int32 id_; - enum Flags : std::int32_t { GROUPED_MASK = 1, THREAD_MASK = 2 }; mutable int32 var0; channels_exportMessageLink(int32 flags_, bool grouped_, bool thread_, object_ptr &&channel_, int32 id_); @@ -37069,7 +37393,6 @@ class channels_getAdminedPublicChannels final : public Function { bool by_location_; bool check_limit_; bool for_personal_; - enum Flags : std::int32_t { BY_LOCATION_MASK = 1, CHECK_LIMIT_MASK = 2, FOR_PERSONAL_MASK = 4 }; mutable int32 var0; channels_getAdminedPublicChannels(int32 flags_, bool by_location_, bool check_limit_, bool for_personal_); @@ -37409,7 +37732,6 @@ class channels_getSendAs final : public Function { int32 flags_; bool for_paid_reactions_; object_ptr peer_; - enum Flags : std::int32_t { FOR_PAID_REACTIONS_MASK = 1 }; mutable int32 var0; channels_getSendAs(int32 flags_, bool for_paid_reactions_, object_ptr &&peer_); @@ -37577,7 +37899,6 @@ class channels_reorderPinnedForumTopics final : public Function { bool force_; object_ptr channel_; array order_; - enum Flags : std::int32_t { FORCE_MASK = 1 }; mutable int32 var0; channels_reorderPinnedForumTopics(int32 flags_, bool force_, object_ptr &&channel_, array &&order_); @@ -37877,6 +38198,34 @@ class InputChannel; class Updates; +class channels_toggleAutotranslation final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr channel_; + bool enabled_; + + channels_toggleAutotranslation(object_ptr &&channel_, bool enabled_); + + static const std::int32_t ID = 377471137; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + +class InputChannel; + +class Updates; + class channels_toggleForum final : public Function { std::int32_t get_id() const final { return ID; @@ -38027,7 +38376,6 @@ class channels_toggleSignatures final : public Function { bool signatures_enabled_; bool profiles_enabled_; object_ptr channel_; - enum Flags : std::int32_t { SIGNATURES_ENABLED_MASK = 1, PROFILES_ENABLED_MASK = 2 }; mutable int32 var0; channels_toggleSignatures(int32 flags_, bool signatures_enabled_, bool profiles_enabled_, object_ptr &&channel_); @@ -38143,7 +38491,7 @@ class channels_updateColor final : public Function { object_ptr channel_; int32 color_; int64 background_emoji_id_; - enum Flags : std::int32_t { FOR_PROFILE_MASK = 2, COLOR_MASK = 4, BACKGROUND_EMOJI_ID_MASK = 1 }; + enum Flags : std::int32_t { COLOR_MASK = 4, BACKGROUND_EMOJI_ID_MASK = 1 }; mutable int32 var0; channels_updateColor(int32 flags_, bool for_profile_, object_ptr &&channel_, int32 color_, int64 background_emoji_id_); @@ -38628,7 +38976,6 @@ class contacts_addContact final : public Function { string first_name_; string last_name_; string phone_; - enum Flags : std::int32_t { ADD_PHONE_PRIVACY_EXCEPTION_MASK = 1 }; mutable int32 var0; contacts_addContact(int32 flags_, bool add_phone_privacy_exception_, object_ptr &&id_, string const &first_name_, string const &last_name_, string const &phone_); @@ -38657,7 +39004,6 @@ class contacts_block final : public Function { int32 flags_; bool my_stories_from_; object_ptr id_; - enum Flags : std::int32_t { MY_STORIES_FROM_MASK = 1 }; mutable int32 var0; contacts_block(int32 flags_, bool my_stories_from_, object_ptr &&id_); @@ -38688,7 +39034,6 @@ class contacts_blockFromReplies final : public Function { bool delete_history_; bool report_spam_; int32 msg_id_; - enum Flags : std::int32_t { DELETE_MESSAGE_MASK = 1, DELETE_HISTORY_MASK = 2, REPORT_SPAM_MASK = 4 }; mutable int32 var0; contacts_blockFromReplies(int32 flags_, bool delete_message_, bool delete_history_, bool report_spam_, int32 msg_id_); @@ -38835,7 +39180,6 @@ class contacts_getBlocked final : public Function { bool my_stories_from_; int32 offset_; int32 limit_; - enum Flags : std::int32_t { MY_STORIES_FROM_MASK = 1 }; mutable int32 var0; contacts_getBlocked(int32 flags_, bool my_stories_from_, int32 offset_, int32 limit_); @@ -38915,7 +39259,7 @@ class contacts_getLocated final : public Function { bool background_; object_ptr geo_point_; int32 self_expires_; - enum Flags : std::int32_t { BACKGROUND_MASK = 2, SELF_EXPIRES_MASK = 1 }; + enum Flags : std::int32_t { SELF_EXPIRES_MASK = 1 }; mutable int32 var0; contacts_getLocated(int32 flags_, bool background_, object_ptr &&geo_point_, int32 self_expires_); @@ -39023,7 +39367,6 @@ class contacts_getTopPeers final : public Function { int32 offset_; int32 limit_; int64 hash_; - enum Flags : std::int32_t { CORRESPONDENTS_MASK = 1, BOTS_PM_MASK = 2, BOTS_INLINE_MASK = 4, PHONE_CALLS_MASK = 8, FORWARD_USERS_MASK = 16, FORWARD_CHATS_MASK = 32, GROUPS_MASK = 1024, CHANNELS_MASK = 32768, BOTS_APP_MASK = 65536 }; mutable int32 var0; contacts_getTopPeers(int32 flags_, bool correspondents_, bool bots_pm_, bool bots_inline_, bool phone_calls_, bool forward_users_, bool forward_chats_, bool groups_, bool channels_, bool bots_app_, int32 offset_, int32 limit_, int64 hash_); @@ -39233,7 +39576,6 @@ class contacts_setBlocked final : public Function { bool my_stories_from_; array> id_; int32 limit_; - enum Flags : std::int32_t { MY_STORIES_FROM_MASK = 1 }; mutable int32 var0; contacts_setBlocked(int32 flags_, bool my_stories_from_, array> &&id_, int32 limit_); @@ -39285,7 +39627,6 @@ class contacts_unblock final : public Function { int32 flags_; bool my_stories_from_; object_ptr id_; - enum Flags : std::int32_t { MY_STORIES_FROM_MASK = 1 }; mutable int32 var0; contacts_unblock(int32 flags_, bool my_stories_from_, object_ptr &&id_); @@ -40242,7 +40583,7 @@ class messages_acceptUrlAuth final : public Function { int32 msg_id_; int32 button_id_; string url_; - enum Flags : std::int32_t { WRITE_ALLOWED_MASK = 1, PEER_MASK = 2, MSG_ID_MASK = 2, BUTTON_ID_MASK = 2, URL_MASK = 4 }; + enum Flags : std::int32_t { PEER_MASK = 2, MSG_ID_MASK = 2, BUTTON_ID_MASK = 2, URL_MASK = 4 }; mutable int32 var0; messages_acceptUrlAuth(int32 flags_, bool write_allowed_, object_ptr &&peer_, int32 msg_id_, int32 button_id_, string const &url_); @@ -40437,7 +40778,6 @@ class messages_clearRecentStickers final : public Function { public: int32 flags_; bool attached_; - enum Flags : std::int32_t { ATTACHED_MASK = 1 }; mutable int32 var0; messages_clearRecentStickers(int32 flags_, bool attached_); @@ -40465,7 +40805,6 @@ class messages_clickSponsoredMessage final : public Function { bool media_; bool fullscreen_; bytes random_id_; - enum Flags : std::int32_t { MEDIA_MASK = 1, FULLSCREEN_MASK = 2 }; mutable int32 var0; messages_clickSponsoredMessage(int32 flags_, bool media_, bool fullscreen_, bytes &&random_id_); @@ -40552,7 +40891,6 @@ class messages_deleteChatUser final : public Function { bool revoke_history_; int64 chat_id_; object_ptr user_id_; - enum Flags : std::int32_t { REVOKE_HISTORY_MASK = 1 }; mutable int32 var0; messages_deleteChatUser(int32 flags_, bool revoke_history_, int64 chat_id_, object_ptr &&user_id_); @@ -40641,7 +40979,7 @@ class messages_deleteHistory final : public Function { int32 max_id_; int32 min_date_; int32 max_date_; - enum Flags : std::int32_t { JUST_CLEAR_MASK = 1, REVOKE_MASK = 2, MIN_DATE_MASK = 4, MAX_DATE_MASK = 8 }; + enum Flags : std::int32_t { MIN_DATE_MASK = 4, MAX_DATE_MASK = 8 }; mutable int32 var0; messages_deleteHistory(int32 flags_, bool just_clear_, bool revoke_, object_ptr &&peer_, int32 max_id_, int32 min_date_, int32 max_date_); @@ -40670,7 +41008,6 @@ class messages_deleteMessages final : public Function { int32 flags_; bool revoke_; array id_; - enum Flags : std::int32_t { REVOKE_MASK = 1 }; mutable int32 var0; messages_deleteMessages(int32 flags_, bool revoke_, array &&id_); @@ -40698,7 +41035,6 @@ class messages_deletePhoneCallHistory final : public Function { public: int32 flags_; bool revoke_; - enum Flags : std::int32_t { REVOKE_MASK = 1 }; mutable int32 var0; messages_deletePhoneCallHistory(int32 flags_, bool revoke_); @@ -40863,7 +41199,6 @@ class messages_discardEncryption final : public Function { int32 flags_; bool delete_history_; int32 chat_id_; - enum Flags : std::int32_t { DELETE_HISTORY_MASK = 1 }; mutable int32 var0; messages_discardEncryption(int32 flags_, bool delete_history_, int32 chat_id_); @@ -41036,7 +41371,7 @@ class messages_editExportedChatInvite final : public Function { int32 usage_limit_; bool request_needed_; string title_; - enum Flags : std::int32_t { REVOKED_MASK = 4, EXPIRE_DATE_MASK = 1, USAGE_LIMIT_MASK = 2, REQUEST_NEEDED_MASK = 8, TITLE_MASK = 16 }; + enum Flags : std::int32_t { EXPIRE_DATE_MASK = 1, USAGE_LIMIT_MASK = 2, REQUEST_NEEDED_MASK = 8, TITLE_MASK = 16 }; mutable int32 var0; messages_editExportedChatInvite(int32 flags_, bool revoked_, object_ptr &&peer_, string const &link_, int32 expire_date_, int32 usage_limit_, bool request_needed_, string const &title_); @@ -41107,7 +41442,7 @@ class messages_editInlineBotMessage final : public Function { object_ptr media_; object_ptr reply_markup_; array> entities_; - enum Flags : std::int32_t { NO_WEBPAGE_MASK = 2, INVERT_MEDIA_MASK = 65536, MESSAGE_MASK = 2048, MEDIA_MASK = 16384, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8 }; + enum Flags : std::int32_t { MESSAGE_MASK = 2048, MEDIA_MASK = 16384, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8 }; mutable int32 var0; messages_editInlineBotMessage(int32 flags_, bool no_webpage_, bool invert_media_, object_ptr &&id_, string const &message_, object_ptr &&media_, object_ptr &&reply_markup_, array> &&entities_); @@ -41152,7 +41487,7 @@ class messages_editMessage final : public Function { array> entities_; int32 schedule_date_; int32 quick_reply_shortcut_id_; - enum Flags : std::int32_t { NO_WEBPAGE_MASK = 2, INVERT_MEDIA_MASK = 65536, MESSAGE_MASK = 2048, MEDIA_MASK = 16384, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 32768, QUICK_REPLY_SHORTCUT_ID_MASK = 131072 }; + enum Flags : std::int32_t { MESSAGE_MASK = 2048, MEDIA_MASK = 16384, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 32768, QUICK_REPLY_SHORTCUT_ID_MASK = 131072 }; mutable int32 var0; messages_editMessage(int32 flags_, bool no_webpage_, bool invert_media_, object_ptr &&peer_, int32 id_, string const &message_, object_ptr &&media_, object_ptr &&reply_markup_, array> &&entities_, int32 schedule_date_, int32 quick_reply_shortcut_id_); @@ -41214,7 +41549,7 @@ class messages_exportChatInvite final : public Function { int32 usage_limit_; string title_; object_ptr subscription_pricing_; - enum Flags : std::int32_t { LEGACY_REVOKE_PERMANENT_MASK = 4, REQUEST_NEEDED_MASK = 8, EXPIRE_DATE_MASK = 1, USAGE_LIMIT_MASK = 2, TITLE_MASK = 16, SUBSCRIPTION_PRICING_MASK = 32 }; + enum Flags : std::int32_t { EXPIRE_DATE_MASK = 1, USAGE_LIMIT_MASK = 2, TITLE_MASK = 16, SUBSCRIPTION_PRICING_MASK = 32 }; mutable int32 var0; messages_exportChatInvite(int32 flags_, bool legacy_revoke_permanent_, bool request_needed_, object_ptr &&peer_, int32 expire_date_, int32 usage_limit_, string const &title_, object_ptr &&subscription_pricing_); @@ -41288,7 +41623,7 @@ class messages_forwardMessages final : public Function { object_ptr quick_reply_shortcut_; int32 video_timestamp_; int64 allow_paid_stars_; - enum Flags : std::int32_t { SILENT_MASK = 32, BACKGROUND_MASK = 64, WITH_MY_SCORE_MASK = 256, DROP_AUTHOR_MASK = 2048, DROP_MEDIA_CAPTIONS_MASK = 4096, NOFORWARDS_MASK = 16384, ALLOW_PAID_FLOODSKIP_MASK = 524288, TOP_MSG_ID_MASK = 512, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, VIDEO_TIMESTAMP_MASK = 1048576, ALLOW_PAID_STARS_MASK = 2097152 }; + enum Flags : std::int32_t { TOP_MSG_ID_MASK = 512, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, VIDEO_TIMESTAMP_MASK = 1048576, ALLOW_PAID_STARS_MASK = 2097152 }; mutable int32 var0; messages_forwardMessages(int32 flags_, bool silent_, bool background_, bool with_my_score_, bool drop_author_, bool drop_media_captions_, bool noforwards_, bool allow_paid_floodskip_, object_ptr &&from_peer_, array &&id_, array &&random_id_, object_ptr &&to_peer_, int32 top_msg_id_, int32 schedule_date_, object_ptr &&send_as_, object_ptr &&quick_reply_shortcut_, int32 video_timestamp_, int64 allow_paid_stars_); @@ -41393,7 +41728,6 @@ class messages_getArchivedStickers final : public Function { bool emojis_; int64 offset_id_; int32 limit_; - enum Flags : std::int32_t { MASKS_MASK = 1, EMOJIS_MASK = 2 }; mutable int32 var0; messages_getArchivedStickers(int32 flags_, bool masks_, bool emojis_, int64 offset_id_, int32 limit_); @@ -41586,7 +41920,7 @@ class messages_getBotCallbackAnswer final : public Function { int32 msg_id_; bytes data_; object_ptr password_; - enum Flags : std::int32_t { GAME_MASK = 2, DATA_MASK = 1, PASSWORD_MASK = 4 }; + enum Flags : std::int32_t { DATA_MASK = 1, PASSWORD_MASK = 4 }; mutable int32 var0; messages_getBotCallbackAnswer(int32 flags_, bool game_, object_ptr &&peer_, int32 msg_id_, bytes &&data_, object_ptr &&password_); @@ -41625,7 +41959,7 @@ class messages_getChatInviteImporters final : public Function { int32 offset_date_; object_ptr offset_user_; int32 limit_; - enum Flags : std::int32_t { REQUESTED_MASK = 1, SUBSCRIPTION_EXPIRED_MASK = 8, LINK_MASK = 2, Q_MASK = 4 }; + enum Flags : std::int32_t { LINK_MASK = 2, Q_MASK = 4 }; mutable int32 var0; messages_getChatInviteImporters(int32 flags_, bool requested_, bool subscription_expired_, object_ptr &&peer_, string const &link_, string const &q_, int32 offset_date_, object_ptr &&offset_user_, int32 limit_); @@ -41857,7 +42191,7 @@ class messages_getDialogs final : public Function { object_ptr offset_peer_; int32 limit_; int64 hash_; - enum Flags : std::int32_t { EXCLUDE_PINNED_MASK = 1, FOLDER_ID_MASK = 2 }; + enum Flags : std::int32_t { FOLDER_ID_MASK = 2 }; mutable int32 var0; messages_getDialogs(int32 flags_, bool exclude_pinned_, int32 folder_id_, int32 offset_date_, int32 offset_id_, object_ptr &&offset_peer_, int32 limit_, int64 hash_); @@ -42203,7 +42537,7 @@ class messages_getExportedChatInvites final : public Function { int32 offset_date_; string offset_link_; int32 limit_; - enum Flags : std::int32_t { REVOKED_MASK = 8, OFFSET_DATE_MASK = 4, OFFSET_LINK_MASK = 4 }; + enum Flags : std::int32_t { OFFSET_DATE_MASK = 4, OFFSET_LINK_MASK = 4 }; mutable int32 var0; messages_getExportedChatInvites(int32 flags_, bool revoked_, object_ptr &&peer_, object_ptr &&admin_id_, int32 offset_date_, string const &offset_link_, int32 limit_); @@ -43153,7 +43487,6 @@ class messages_getRecentStickers final : public Function { int32 flags_; bool attached_; int64 hash_; - enum Flags : std::int32_t { ATTACHED_MASK = 1 }; mutable int32 var0; messages_getRecentStickers(int32 flags_, bool attached_, int64 hash_); @@ -43223,7 +43556,6 @@ class messages_getSavedDialogs final : public Function { object_ptr offset_peer_; int32 limit_; int64 hash_; - enum Flags : std::int32_t { EXCLUDE_PINNED_MASK = 1 }; mutable int32 var0; messages_getSavedDialogs(int32 flags_, bool exclude_pinned_, int32 offset_date_, int32 offset_id_, object_ptr &&offset_peer_, int32 limit_, int64 hash_); @@ -43788,7 +44120,7 @@ class messages_hideAllChatJoinRequests final : public Function { bool approved_; object_ptr peer_; string link_; - enum Flags : std::int32_t { APPROVED_MASK = 1, LINK_MASK = 2 }; + enum Flags : std::int32_t { LINK_MASK = 2 }; mutable int32 var0; messages_hideAllChatJoinRequests(int32 flags_, bool approved_, object_ptr &&peer_, string const &link_); @@ -43822,7 +44154,6 @@ class messages_hideChatJoinRequest final : public Function { bool approved_; object_ptr peer_; object_ptr user_id_; - enum Flags : std::int32_t { APPROVED_MASK = 1 }; mutable int32 var0; messages_hideChatJoinRequest(int32 flags_, bool approved_, object_ptr &&peer_, object_ptr &&user_id_); @@ -43960,7 +44291,6 @@ class messages_markDialogUnread final : public Function { int32 flags_; bool unread_; object_ptr peer_; - enum Flags : std::int32_t { UNREAD_MASK = 1 }; mutable int32 var0; messages_markDialogUnread(int32 flags_, bool unread_, object_ptr &&peer_); @@ -44022,7 +44352,7 @@ class messages_prolongWebView final : public Function { int64 query_id_; object_ptr reply_to_; object_ptr send_as_; - enum Flags : std::int32_t { SILENT_MASK = 32, REPLY_TO_MASK = 1, SEND_AS_MASK = 8192 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 1, SEND_AS_MASK = 8192 }; mutable int32 var0; messages_prolongWebView(int32 flags_, bool silent_, object_ptr &&peer_, object_ptr &&bot_, int64 query_id_, object_ptr &&reply_to_, object_ptr &&send_as_); @@ -44319,7 +44649,6 @@ class messages_reorderPinnedDialogs final : public Function { bool force_; int32 folder_id_; array> order_; - enum Flags : std::int32_t { FORCE_MASK = 1 }; mutable int32 var0; messages_reorderPinnedDialogs(int32 flags_, bool force_, int32 folder_id_, array> &&order_); @@ -44348,7 +44677,6 @@ class messages_reorderPinnedSavedDialogs final : public Function { int32 flags_; bool force_; array> order_; - enum Flags : std::int32_t { FORCE_MASK = 1 }; mutable int32 var0; messages_reorderPinnedSavedDialogs(int32 flags_, bool force_, array> &&order_); @@ -44399,7 +44727,6 @@ class messages_reorderStickerSets final : public Function { bool masks_; bool emojis_; array order_; - enum Flags : std::int32_t { MASKS_MASK = 1, EMOJIS_MASK = 2 }; mutable int32 var0; messages_reorderStickerSets(int32 flags_, bool masks_, bool emojis_, array &&order_); @@ -44484,7 +44811,6 @@ class messages_reportMessagesDelivery final : public Function { bool push_; object_ptr peer_; array id_; - enum Flags : std::int32_t { PUSH_MASK = 1 }; mutable int32 var0; messages_reportMessagesDelivery(int32 flags_, bool push_, object_ptr &&peer_, array &&id_); @@ -44603,7 +44929,7 @@ class messages_requestAppWebView final : public Function { string start_param_; object_ptr theme_params_; string platform_; - enum Flags : std::int32_t { WRITE_ALLOWED_MASK = 1, COMPACT_MASK = 128, FULLSCREEN_MASK = 256, START_PARAM_MASK = 2, THEME_PARAMS_MASK = 4 }; + enum Flags : std::int32_t { START_PARAM_MASK = 2, THEME_PARAMS_MASK = 4 }; mutable int32 var0; messages_requestAppWebView(int32 flags_, bool write_allowed_, bool compact_, bool fullscreen_, object_ptr &&peer_, object_ptr &&app_, string const &start_param_, object_ptr &&theme_params_, string const &platform_); @@ -44672,7 +44998,7 @@ class messages_requestMainWebView final : public Function { string start_param_; object_ptr theme_params_; string platform_; - enum Flags : std::int32_t { COMPACT_MASK = 128, FULLSCREEN_MASK = 256, START_PARAM_MASK = 2, THEME_PARAMS_MASK = 1 }; + enum Flags : std::int32_t { START_PARAM_MASK = 2, THEME_PARAMS_MASK = 1 }; mutable int32 var0; messages_requestMainWebView(int32 flags_, bool compact_, bool fullscreen_, object_ptr &&peer_, object_ptr &&bot_, string const &start_param_, object_ptr &&theme_params_, string const &platform_); @@ -44712,7 +45038,7 @@ class messages_requestSimpleWebView final : public Function { string start_param_; object_ptr theme_params_; string platform_; - enum Flags : std::int32_t { FROM_SWITCH_WEBVIEW_MASK = 2, FROM_SIDE_MENU_MASK = 4, COMPACT_MASK = 128, FULLSCREEN_MASK = 256, URL_MASK = 8, START_PARAM_MASK = 16, THEME_PARAMS_MASK = 1 }; + enum Flags : std::int32_t { URL_MASK = 8, START_PARAM_MASK = 16, THEME_PARAMS_MASK = 1 }; mutable int32 var0; messages_requestSimpleWebView(int32 flags_, bool from_switch_webview_, bool from_side_menu_, bool compact_, bool fullscreen_, object_ptr &&bot_, string const &url_, string const &start_param_, object_ptr &&theme_params_, string const &platform_); @@ -44792,7 +45118,7 @@ class messages_requestWebView final : public Function { string platform_; object_ptr reply_to_; object_ptr send_as_; - enum Flags : std::int32_t { FROM_BOT_MENU_MASK = 16, SILENT_MASK = 32, COMPACT_MASK = 128, FULLSCREEN_MASK = 256, URL_MASK = 2, START_PARAM_MASK = 8, THEME_PARAMS_MASK = 4, REPLY_TO_MASK = 1, SEND_AS_MASK = 8192 }; + enum Flags : std::int32_t { URL_MASK = 2, START_PARAM_MASK = 8, THEME_PARAMS_MASK = 4, REPLY_TO_MASK = 1, SEND_AS_MASK = 8192 }; mutable int32 var0; messages_requestWebView(int32 flags_, bool from_bot_menu_, bool silent_, bool compact_, bool fullscreen_, object_ptr &&peer_, object_ptr &&bot_, string const &url_, string const &start_param_, object_ptr &&theme_params_, string const &platform_, object_ptr &&reply_to_, object_ptr &&send_as_); @@ -44859,7 +45185,7 @@ class messages_saveDraft final : public Function { array> entities_; object_ptr media_; int64 effect_; - enum Flags : std::int32_t { NO_WEBPAGE_MASK = 2, INVERT_MEDIA_MASK = 64, REPLY_TO_MASK = 16, ENTITIES_MASK = 8, MEDIA_MASK = 32, EFFECT_MASK = 128 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 16, ENTITIES_MASK = 8, MEDIA_MASK = 32, EFFECT_MASK = 128 }; mutable int32 var0; messages_saveDraft(int32 flags_, bool no_webpage_, bool invert_media_, object_ptr &&reply_to_, object_ptr &&peer_, string const &message_, array> &&entities_, object_ptr &&media_, int64 effect_); @@ -44951,7 +45277,6 @@ class messages_saveRecentSticker final : public Function { bool attached_; object_ptr id_; bool unsave_; - enum Flags : std::int32_t { ATTACHED_MASK = 1 }; mutable int32 var0; messages_saveRecentSticker(int32 flags_, bool attached_, object_ptr &&id_, bool unsave_); @@ -45055,7 +45380,6 @@ class messages_searchEmojiStickerSets final : public Function { bool exclude_featured_; string q_; int64 hash_; - enum Flags : std::int32_t { EXCLUDE_FEATURED_MASK = 1 }; mutable int32 var0; messages_searchEmojiStickerSets(int32 flags_, bool exclude_featured_, string const &q_, int64 hash_); @@ -45098,7 +45422,7 @@ class messages_searchGlobal final : public Function { object_ptr offset_peer_; int32 offset_id_; int32 limit_; - enum Flags : std::int32_t { BROADCASTS_ONLY_MASK = 2, GROUPS_ONLY_MASK = 4, USERS_ONLY_MASK = 8, FOLDER_ID_MASK = 1 }; + enum Flags : std::int32_t { FOLDER_ID_MASK = 1 }; mutable int32 var0; messages_searchGlobal(int32 flags_, bool broadcasts_only_, bool groups_only_, bool users_only_, int32 folder_id_, string const &q_, object_ptr &&filter_, int32 min_date_, int32 max_date_, int32 offset_rate_, object_ptr &&offset_peer_, int32 offset_id_, int32 limit_); @@ -45157,7 +45481,6 @@ class messages_searchStickerSets final : public Function { bool exclude_featured_; string q_; int64 hash_; - enum Flags : std::int32_t { EXCLUDE_FEATURED_MASK = 1 }; mutable int32 var0; messages_searchStickerSets(int32 flags_, bool exclude_featured_, string const &q_, int64 hash_); @@ -45191,7 +45514,6 @@ class messages_searchStickers final : public Function { int32 offset_; int32 limit_; int64 hash_; - enum Flags : std::int32_t { EMOJIS_MASK = 1 }; mutable int32 var0; messages_searchStickers(int32 flags_, bool emojis_, string const &q_, string const &emoticon_, array &&lang_code_, int32 offset_, int32 limit_, int64 hash_); @@ -45254,7 +45576,6 @@ class messages_sendEncrypted final : public Function { object_ptr peer_; int64 random_id_; bytes data_; - enum Flags : std::int32_t { SILENT_MASK = 1 }; mutable int32 var0; messages_sendEncrypted(int32 flags_, bool silent_, object_ptr &&peer_, int64 random_id_, bytes &&data_); @@ -45290,7 +45611,6 @@ class messages_sendEncryptedFile final : public Function { int64 random_id_; bytes data_; object_ptr file_; - enum Flags : std::int32_t { SILENT_MASK = 1 }; mutable int32 var0; messages_sendEncryptedFile(int32 flags_, bool silent_, object_ptr &&peer_, int64 random_id_, bytes &&data_, object_ptr &&file_); @@ -45365,7 +45685,7 @@ class messages_sendInlineBotResult final : public Function { object_ptr send_as_; object_ptr quick_reply_shortcut_; int64 allow_paid_stars_; - enum Flags : std::int32_t { SILENT_MASK = 32, BACKGROUND_MASK = 64, CLEAR_DRAFT_MASK = 128, HIDE_VIA_MASK = 2048, REPLY_TO_MASK = 1, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, ALLOW_PAID_STARS_MASK = 2097152 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 1, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, ALLOW_PAID_STARS_MASK = 2097152 }; mutable int32 var0; messages_sendInlineBotResult(int32 flags_, bool silent_, bool background_, bool clear_draft_, bool hide_via_, object_ptr &&peer_, object_ptr &&reply_to_, int64 random_id_, int64 query_id_, string const &id_, int32 schedule_date_, object_ptr &&send_as_, object_ptr &&quick_reply_shortcut_, int64 allow_paid_stars_); @@ -45423,7 +45743,7 @@ class messages_sendMedia final : public Function { object_ptr quick_reply_shortcut_; int64 effect_; int64 allow_paid_stars_; - enum Flags : std::int32_t { SILENT_MASK = 32, BACKGROUND_MASK = 64, CLEAR_DRAFT_MASK = 128, NOFORWARDS_MASK = 16384, UPDATE_STICKERSETS_ORDER_MASK = 32768, INVERT_MEDIA_MASK = 65536, ALLOW_PAID_FLOODSKIP_MASK = 524288, REPLY_TO_MASK = 1, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 1, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; mutable int32 var0; messages_sendMedia(int32 flags_, bool silent_, bool background_, bool clear_draft_, bool noforwards_, bool update_stickersets_order_, bool invert_media_, bool allow_paid_floodskip_, object_ptr &&peer_, object_ptr &&reply_to_, object_ptr &&media_, string const &message_, int64 random_id_, object_ptr &&reply_markup_, array> &&entities_, int32 schedule_date_, object_ptr &&send_as_, object_ptr &&quick_reply_shortcut_, int64 effect_, int64 allow_paid_stars_); @@ -45479,7 +45799,7 @@ class messages_sendMessage final : public Function { object_ptr quick_reply_shortcut_; int64 effect_; int64 allow_paid_stars_; - enum Flags : std::int32_t { NO_WEBPAGE_MASK = 2, SILENT_MASK = 32, BACKGROUND_MASK = 64, CLEAR_DRAFT_MASK = 128, NOFORWARDS_MASK = 16384, UPDATE_STICKERSETS_ORDER_MASK = 32768, INVERT_MEDIA_MASK = 65536, ALLOW_PAID_FLOODSKIP_MASK = 524288, REPLY_TO_MASK = 1, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 1, REPLY_MARKUP_MASK = 4, ENTITIES_MASK = 8, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; mutable int32 var0; messages_sendMessage(int32 flags_, bool no_webpage_, bool silent_, bool background_, bool clear_draft_, bool noforwards_, bool update_stickersets_order_, bool invert_media_, bool allow_paid_floodskip_, object_ptr &&peer_, object_ptr &&reply_to_, string const &message_, int64 random_id_, object_ptr &&reply_markup_, array> &&entities_, int32 schedule_date_, object_ptr &&send_as_, object_ptr &&quick_reply_shortcut_, int64 effect_, int64 allow_paid_stars_); @@ -45529,7 +45849,7 @@ class messages_sendMultiMedia final : public Function { object_ptr quick_reply_shortcut_; int64 effect_; int64 allow_paid_stars_; - enum Flags : std::int32_t { SILENT_MASK = 32, BACKGROUND_MASK = 64, CLEAR_DRAFT_MASK = 128, NOFORWARDS_MASK = 16384, UPDATE_STICKERSETS_ORDER_MASK = 32768, INVERT_MEDIA_MASK = 65536, ALLOW_PAID_FLOODSKIP_MASK = 524288, REPLY_TO_MASK = 1, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; + enum Flags : std::int32_t { REPLY_TO_MASK = 1, SCHEDULE_DATE_MASK = 1024, SEND_AS_MASK = 8192, QUICK_REPLY_SHORTCUT_MASK = 131072, EFFECT_MASK = 262144, ALLOW_PAID_STARS_MASK = 2097152 }; mutable int32 var0; messages_sendMultiMedia(int32 flags_, bool silent_, bool background_, bool clear_draft_, bool noforwards_, bool update_stickersets_order_, bool invert_media_, bool allow_paid_floodskip_, object_ptr &&peer_, object_ptr &&reply_to_, array> &&multi_media_, int32 schedule_date_, object_ptr &&send_as_, object_ptr &&quick_reply_shortcut_, int64 effect_, int64 allow_paid_stars_); @@ -45631,7 +45951,7 @@ class messages_sendReaction final : public Function { object_ptr peer_; int32 msg_id_; array> reaction_; - enum Flags : std::int32_t { BIG_MASK = 2, ADD_TO_RECENT_MASK = 4, REACTION_MASK = 1 }; + enum Flags : std::int32_t { REACTION_MASK = 1 }; mutable int32 var0; messages_sendReaction(int32 flags_, bool big_, bool add_to_recent_, object_ptr &&peer_, int32 msg_id_, array> &&reaction_); @@ -45807,7 +46127,7 @@ class messages_setBotCallbackAnswer final : public Function { string message_; string url_; int32 cache_time_; - enum Flags : std::int32_t { ALERT_MASK = 2, MESSAGE_MASK = 1, URL_MASK = 4 }; + enum Flags : std::int32_t { MESSAGE_MASK = 1, URL_MASK = 4 }; mutable int32 var0; messages_setBotCallbackAnswer(int32 flags_, bool alert_, int64 query_id_, string const &message_, string const &url_, int32 cache_time_); @@ -45835,7 +46155,7 @@ class messages_setBotPrecheckoutResults final : public Function { bool success_; int64 query_id_; string error_; - enum Flags : std::int32_t { SUCCESS_MASK = 2, ERROR_MASK = 1 }; + enum Flags : std::int32_t { ERROR_MASK = 1 }; mutable int32 var0; messages_setBotPrecheckoutResults(int32 flags_, bool success_, int64 query_id_, string const &error_); @@ -45967,7 +46287,7 @@ class messages_setChatWallPaper final : public Function { object_ptr wallpaper_; object_ptr settings_; int32 id_; - enum Flags : std::int32_t { FOR_BOTH_MASK = 8, REVERT_MASK = 16, WALLPAPER_MASK = 1, SETTINGS_MASK = 4, ID_MASK = 2 }; + enum Flags : std::int32_t { WALLPAPER_MASK = 1, SETTINGS_MASK = 4, ID_MASK = 2 }; mutable int32 var0; messages_setChatWallPaper(int32 flags_, bool for_both_, bool revert_, object_ptr &&peer_, object_ptr &&wallpaper_, object_ptr &&settings_, int32 id_); @@ -46078,7 +46398,6 @@ class messages_setGameScore final : public Function { int32 id_; object_ptr user_id_; int32 score_; - enum Flags : std::int32_t { EDIT_MESSAGE_MASK = 1, FORCE_MASK = 2 }; mutable int32 var0; messages_setGameScore(int32 flags_, bool edit_message_, bool force_, object_ptr &&peer_, int32 id_, object_ptr &&user_id_, int32 score_); @@ -46145,7 +46464,7 @@ class messages_setInlineBotResults final : public Function { string next_offset_; object_ptr switch_pm_; object_ptr switch_webview_; - enum Flags : std::int32_t { GALLERY_MASK = 1, PRIVATE_MASK = 2, NEXT_OFFSET_MASK = 4, SWITCH_PM_MASK = 8, SWITCH_WEBVIEW_MASK = 16 }; + enum Flags : std::int32_t { NEXT_OFFSET_MASK = 4, SWITCH_PM_MASK = 8, SWITCH_WEBVIEW_MASK = 16 }; mutable int32 var0; messages_setInlineBotResults(int32 flags_, bool gallery_, bool private_, int64 query_id_, array> &&results_, int32 cache_time_, string const &next_offset_, object_ptr &&switch_pm_, object_ptr &&switch_webview_); @@ -46179,7 +46498,6 @@ class messages_setInlineGameScore final : public Function { object_ptr id_; object_ptr user_id_; int32 score_; - enum Flags : std::int32_t { EDIT_MESSAGE_MASK = 1, FORCE_MASK = 2 }; mutable int32 var0; messages_setInlineGameScore(int32 flags_, bool edit_message_, bool force_, object_ptr &&id_, object_ptr &&user_id_, int32 score_); @@ -46299,7 +46617,6 @@ class messages_toggleBotInAttachMenu final : public Function { bool write_allowed_; object_ptr bot_; bool enabled_; - enum Flags : std::int32_t { WRITE_ALLOWED_MASK = 1 }; mutable int32 var0; messages_toggleBotInAttachMenu(int32 flags_, bool write_allowed_, object_ptr &&bot_, bool enabled_); @@ -46351,7 +46668,6 @@ class messages_toggleDialogPin final : public Function { int32 flags_; bool pinned_; object_ptr peer_; - enum Flags : std::int32_t { PINNED_MASK = 1 }; mutable int32 var0; messages_toggleDialogPin(int32 flags_, bool pinned_, object_ptr &&peer_); @@ -46437,7 +46753,6 @@ class messages_togglePeerTranslations final : public Function { int32 flags_; bool disabled_; object_ptr peer_; - enum Flags : std::int32_t { DISABLED_MASK = 1 }; mutable int32 var0; messages_togglePeerTranslations(int32 flags_, bool disabled_, object_ptr &&peer_); @@ -46466,7 +46781,6 @@ class messages_toggleSavedDialogPin final : public Function { int32 flags_; bool pinned_; object_ptr peer_; - enum Flags : std::int32_t { PINNED_MASK = 1 }; mutable int32 var0; messages_toggleSavedDialogPin(int32 flags_, bool pinned_, object_ptr &&peer_); @@ -46497,7 +46811,6 @@ class messages_toggleStickerSets final : public Function { bool archive_; bool unarchive_; array> stickersets_; - enum Flags : std::int32_t { UNINSTALL_MASK = 1, ARCHIVE_MASK = 2, UNARCHIVE_MASK = 4 }; mutable int32 var0; messages_toggleStickerSets(int32 flags_, bool uninstall_, bool archive_, bool unarchive_, array> &&stickersets_); @@ -46702,7 +47015,6 @@ class messages_updatePinnedMessage final : public Function { bool pm_oneside_; object_ptr peer_; int32 id_; - enum Flags : std::int32_t { SILENT_MASK = 1, UNPIN_MASK = 2, PM_ONESIDE_MASK = 4 }; mutable int32 var0; messages_updatePinnedMessage(int32 flags_, bool silent_, bool unpin_, bool pm_oneside_, object_ptr &&peer_, int32 id_); @@ -46963,7 +47275,6 @@ class payments_botCancelStarsSubscription final : public Function { bool restore_; object_ptr user_id_; string charge_id_; - enum Flags : std::int32_t { RESTORE_MASK = 1 }; mutable int32 var0; payments_botCancelStarsSubscription(int32 flags_, bool restore_, object_ptr &&user_id_, string const &charge_id_); @@ -47070,7 +47381,6 @@ class payments_clearSavedInfo final : public Function { int32 flags_; bool credentials_; bool info_; - enum Flags : std::int32_t { CREDENTIALS_MASK = 1, INFO_MASK = 2 }; mutable int32 var0; payments_clearSavedInfo(int32 flags_, bool credentials_, bool info_); @@ -47157,7 +47467,6 @@ class payments_editConnectedStarRefBot final : public Function { bool revoked_; object_ptr peer_; string link_; - enum Flags : std::int32_t { REVOKED_MASK = 1 }; mutable int32 var0; payments_editConnectedStarRefBot(int32 flags_, bool revoked_, object_ptr &&peer_, string const &link_); @@ -47435,6 +47744,42 @@ class payments_getPremiumGiftCodeOptions final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class StarGiftAttributeId; + +class payments_resaleStarGifts; + +class payments_getResaleStarGifts final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 flags_; + bool sort_by_price_; + bool sort_by_num_; + int64 attributes_hash_; + int64 gift_id_; + array> attributes_; + string offset_; + int32 limit_; + enum Flags : std::int32_t { ATTRIBUTES_HASH_MASK = 1, ATTRIBUTES_MASK = 8 }; + mutable int32 var0; + + payments_getResaleStarGifts(int32 flags_, bool sort_by_price_, bool sort_by_num_, int64 attributes_hash_, int64 gift_id_, array> &&attributes_, string const &offset_, int32 limit_); + + static const std::int32_t ID = 2053087798; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + class payments_savedInfo; class payments_getSavedInfo final : public Function { @@ -47504,7 +47849,6 @@ class payments_getSavedStarGifts final : public Function { object_ptr peer_; string offset_; int32 limit_; - enum Flags : std::int32_t { EXCLUDE_UNSAVED_MASK = 1, EXCLUDE_SAVED_MASK = 2, EXCLUDE_UNLIMITED_MASK = 4, EXCLUDE_LIMITED_MASK = 8, EXCLUDE_UNIQUE_MASK = 16, SORT_BY_VALUE_MASK = 32 }; mutable int32 var0; payments_getSavedStarGifts(int32 flags_, bool exclude_unsaved_, bool exclude_saved_, bool exclude_unlimited_, bool exclude_limited_, bool exclude_unique_, bool sort_by_value_, object_ptr &&peer_, string const &offset_, int32 limit_); @@ -47694,7 +48038,6 @@ class payments_getStarsRevenueStats final : public Function { int32 flags_; bool dark_; object_ptr peer_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; payments_getStarsRevenueStats(int32 flags_, bool dark_, object_ptr &&peer_); @@ -47784,7 +48127,6 @@ class payments_getStarsSubscriptions final : public Function { bool missing_balance_; object_ptr peer_; string offset_; - enum Flags : std::int32_t { MISSING_BALANCE_MASK = 1 }; mutable int32 var0; payments_getStarsSubscriptions(int32 flags_, bool missing_balance_, object_ptr &&peer_, string const &offset_); @@ -47842,7 +48184,7 @@ class payments_getStarsTransactions final : public Function { object_ptr peer_; string offset_; int32 limit_; - enum Flags : std::int32_t { INBOUND_MASK = 1, OUTBOUND_MASK = 2, ASCENDING_MASK = 4, SUBSCRIPTION_ID_MASK = 8 }; + enum Flags : std::int32_t { SUBSCRIPTION_ID_MASK = 8 }; mutable int32 var0; payments_getStarsTransactions(int32 flags_, bool inbound_, bool outbound_, bool ascending_, string const &subscription_id_, object_ptr &&peer_, string const &offset_, int32 limit_); @@ -47906,7 +48248,6 @@ class payments_getSuggestedStarRefBots final : public Function { object_ptr peer_; string offset_; int32 limit_; - enum Flags : std::int32_t { ORDER_BY_REVENUE_MASK = 1, ORDER_BY_DATE_MASK = 2 }; mutable int32 var0; payments_getSuggestedStarRefBots(int32 flags_, bool order_by_revenue_, bool order_by_date_, object_ptr &&peer_, string const &offset_, int32 limit_); @@ -48019,7 +48360,6 @@ class payments_saveStarGift final : public Function { int32 flags_; bool unsave_; object_ptr stargift_; - enum Flags : std::int32_t { UNSAVE_MASK = 1 }; mutable int32 var0; payments_saveStarGift(int32 flags_, bool unsave_, object_ptr &&stargift_); @@ -48113,7 +48453,6 @@ class payments_toggleChatStarGiftNotifications final : public Function { int32 flags_; bool enabled_; object_ptr peer_; - enum Flags : std::int32_t { ENABLED_MASK = 1 }; mutable int32 var0; payments_toggleChatStarGiftNotifications(int32 flags_, bool enabled_, object_ptr &&peer_); @@ -48193,6 +48532,34 @@ class InputSavedStarGift; class Updates; +class payments_updateStarGiftPrice final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr stargift_; + int64 resell_stars_; + + payments_updateStarGiftPrice(object_ptr &&stargift_, int64 resell_stars_); + + static const std::int32_t ID = 1001301217; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + +class InputSavedStarGift; + +class Updates; + class payments_upgradeStarGift final : public Function { std::int32_t get_id() const final { return ID; @@ -48202,7 +48569,6 @@ class payments_upgradeStarGift final : public Function { int32 flags_; bool keep_original_details_; object_ptr stargift_; - enum Flags : std::int32_t { KEEP_ORIGINAL_DETAILS_MASK = 1 }; mutable int32 var0; payments_upgradeStarGift(int32 flags_, bool keep_original_details_, object_ptr &&stargift_); @@ -48236,7 +48602,6 @@ class payments_validateRequestedInfo final : public Function { bool save_; object_ptr invoice_; object_ptr info_; - enum Flags : std::int32_t { SAVE_MASK = 1 }; mutable int32 var0; payments_validateRequestedInfo(int32 flags_, bool save_, object_ptr &&invoice_, object_ptr &&info_); @@ -48285,7 +48650,7 @@ class phone_acceptCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class inputGroupCall; +class InputGroupCall; class phone_checkGroupCall final : public Function { std::int32_t get_id() const final { @@ -48293,10 +48658,10 @@ class phone_checkGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; array sources_; - phone_checkGroupCall(object_ptr &&call_, array &&sources_); + phone_checkGroupCall(object_ptr &&call_, array &&sources_); static const std::int32_t ID = -1248003721; @@ -48343,9 +48708,9 @@ class phone_confirmCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class inputPhoneCall; +class Updates; -class phone_phoneCall; +class dataJSON; class phone_createConferenceCall final : public Function { std::int32_t get_id() const final { @@ -48353,14 +48718,22 @@ class phone_createConferenceCall final : public Function { } public: - object_ptr peer_; - int64 key_fingerprint_; + int32 flags_; + bool muted_; + bool video_stopped_; + bool join_; + int32 random_id_; + UInt256 public_key_; + bytes block_; + object_ptr params_; + enum Flags : std::int32_t { PUBLIC_KEY_MASK = 8, BLOCK_MASK = 8, PARAMS_MASK = 8 }; + mutable int32 var0; - phone_createConferenceCall(object_ptr &&peer_, int64 key_fingerprint_); + phone_createConferenceCall(int32 flags_, bool muted_, bool video_stopped_, bool join_, int32 random_id_, UInt256 const &public_key_, bytes &&block_, object_ptr &¶ms_); - static const std::int32_t ID = -540472917; + static const std::int32_t ID = 2097431739; - using ReturnType = object_ptr; + using ReturnType = object_ptr; void store(TlStorerCalcLength &s) const final; @@ -48387,7 +48760,7 @@ class phone_createGroupCall final : public Function { int32 random_id_; string title_; int32 schedule_date_; - enum Flags : std::int32_t { RTMP_STREAM_MASK = 4, TITLE_MASK = 1, SCHEDULE_DATE_MASK = 2 }; + enum Flags : std::int32_t { TITLE_MASK = 1, SCHEDULE_DATE_MASK = 2 }; mutable int32 var0; phone_createGroupCall(int32 flags_, bool rtmp_stream_, object_ptr &&peer_, int32 random_id_, string const &title_, int32 schedule_date_); @@ -48405,6 +48778,64 @@ class phone_createGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class Updates; + +class phone_declineConferenceCallInvite final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 msg_id_; + + explicit phone_declineConferenceCallInvite(int32 msg_id_); + + static const std::int32_t ID = 1011325297; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + +class InputGroupCall; + +class Updates; + +class phone_deleteConferenceCallParticipants final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 flags_; + bool only_left_; + bool kick_; + object_ptr call_; + array ids_; + bytes block_; + mutable int32 var0; + + phone_deleteConferenceCallParticipants(int32 flags_, bool only_left_, bool kick_, object_ptr &&call_, array &&ids_, bytes &&block_); + + static const std::int32_t ID = -1935276763; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + class PhoneCallDiscardReason; class Updates; @@ -48423,7 +48854,6 @@ class phone_discardCall final : public Function { int32 duration_; object_ptr reason_; int64 connection_id_; - enum Flags : std::int32_t { VIDEO_MASK = 1 }; mutable int32 var0; phone_discardCall(int32 flags_, bool video_, object_ptr &&peer_, int32 duration_, object_ptr &&reason_, int64 connection_id_); @@ -48441,9 +48871,9 @@ class phone_discardCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_discardGroupCall final : public Function { std::int32_t get_id() const final { @@ -48451,9 +48881,9 @@ class phone_discardGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; - explicit phone_discardGroupCall(object_ptr &&call_); + explicit phone_discardGroupCall(object_ptr &&call_); static const std::int32_t ID = 2054648117; @@ -48468,12 +48898,12 @@ class phone_discardGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + class InputPeer; class Updates; -class inputGroupCall; - class phone_editGroupCallParticipant final : public Function { std::int32_t get_id() const final { return ID; @@ -48481,7 +48911,7 @@ class phone_editGroupCallParticipant final : public Function { public: int32 flags_; - object_ptr call_; + object_ptr call_; object_ptr participant_; bool muted_; int32 volume_; @@ -48492,7 +48922,7 @@ class phone_editGroupCallParticipant final : public Function { enum Flags : std::int32_t { MUTED_MASK = 1, VOLUME_MASK = 2, RAISE_HAND_MASK = 4, VIDEO_STOPPED_MASK = 8, VIDEO_PAUSED_MASK = 16, PRESENTATION_PAUSED_MASK = 32 }; mutable int32 var0; - phone_editGroupCallParticipant(int32 flags_, object_ptr &&call_, object_ptr &&participant_, bool muted_, int32 volume_, bool raise_hand_, bool video_stopped_, bool video_paused_, bool presentation_paused_); + phone_editGroupCallParticipant(int32 flags_, object_ptr &&call_, object_ptr &&participant_, bool muted_, int32 volume_, bool raise_hand_, bool video_stopped_, bool video_paused_, bool presentation_paused_); static const std::int32_t ID = -1524155713; @@ -48507,9 +48937,9 @@ class phone_editGroupCallParticipant final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_editGroupCallTitle final : public Function { std::int32_t get_id() const final { @@ -48517,10 +48947,10 @@ class phone_editGroupCallTitle final : public Function { } public: - object_ptr call_; + object_ptr call_; string title_; - phone_editGroupCallTitle(object_ptr &&call_, string const &title_); + phone_editGroupCallTitle(object_ptr &&call_, string const &title_); static const std::int32_t ID = 480685066; @@ -48535,7 +48965,7 @@ class phone_editGroupCallTitle final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class inputGroupCall; +class InputGroupCall; class phone_exportedGroupCallInvite; @@ -48547,11 +48977,10 @@ class phone_exportGroupCallInvite final : public Function { public: int32 flags_; bool can_self_unmute_; - object_ptr call_; - enum Flags : std::int32_t { CAN_SELF_UNMUTE_MASK = 1 }; + object_ptr call_; mutable int32 var0; - phone_exportGroupCallInvite(int32 flags_, bool can_self_unmute_, object_ptr &&call_); + phone_exportGroupCallInvite(int32 flags_, bool can_self_unmute_, object_ptr &&call_); static const std::int32_t ID = -425040769; @@ -48588,7 +49017,7 @@ class phone_getCallConfig final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class inputGroupCall; +class InputGroupCall; class phone_groupCall; @@ -48598,10 +49027,10 @@ class phone_getGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; int32 limit_; - phone_getGroupCall(object_ptr &&call_, int32 limit_); + phone_getGroupCall(object_ptr &&call_, int32 limit_); static const std::int32_t ID = 68699611; @@ -48616,6 +49045,36 @@ class phone_getGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + +class Updates; + +class phone_getGroupCallChainBlocks final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr call_; + int32 sub_chain_id_; + int32 offset_; + int32 limit_; + + phone_getGroupCallChainBlocks(object_ptr &&call_, int32 sub_chain_id_, int32 offset_, int32 limit_); + + static const std::int32_t ID = -291534682; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + class InputPeer; class phone_joinAsPeers; @@ -48643,7 +49102,7 @@ class phone_getGroupCallJoinAs final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class inputGroupCall; +class InputGroupCall; class phone_groupCallStreamChannels; @@ -48653,9 +49112,9 @@ class phone_getGroupCallStreamChannels final : public Function { } public: - object_ptr call_; + object_ptr call_; - explicit phone_getGroupCallStreamChannels(object_ptr &&call_); + explicit phone_getGroupCallStreamChannels(object_ptr &&call_); static const std::int32_t ID = 447879488; @@ -48698,9 +49157,9 @@ class phone_getGroupCallStreamRtmpUrl final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class InputPeer; +class InputGroupCall; -class inputGroupCall; +class InputPeer; class phone_groupParticipants; @@ -48710,13 +49169,13 @@ class phone_getGroupParticipants final : public Function { } public: - object_ptr call_; + object_ptr call_; array> ids_; array sources_; string offset_; int32 limit_; - phone_getGroupParticipants(object_ptr &&call_, array> &&ids_, array &&sources_, string const &offset_, int32 limit_); + phone_getGroupParticipants(object_ptr &&call_, array> &&ids_, array &&sources_, string const &offset_, int32 limit_); static const std::int32_t ID = -984033109; @@ -48731,11 +49190,44 @@ class phone_getGroupParticipants final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + class InputUser; class Updates; -class inputGroupCall; +class phone_inviteConferenceCallParticipant final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + int32 flags_; + bool video_; + object_ptr call_; + object_ptr user_id_; + mutable int32 var0; + + phone_inviteConferenceCallParticipant(int32 flags_, bool video_, object_ptr &&call_, object_ptr &&user_id_); + + static const std::int32_t ID = -1124981115; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + +class InputGroupCall; + +class InputUser; + +class Updates; class phone_inviteToGroupCall final : public Function { std::int32_t get_id() const final { @@ -48743,10 +49235,10 @@ class phone_inviteToGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; array> users_; - phone_inviteToGroupCall(object_ptr &&call_, array> &&users_); + phone_inviteToGroupCall(object_ptr &&call_, array> &&users_); static const std::int32_t ID = 2067345760; @@ -48761,14 +49253,14 @@ class phone_inviteToGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + class InputPeer; class Updates; class dataJSON; -class inputGroupCall; - class phone_joinGroupCall final : public Function { std::int32_t get_id() const final { return ID; @@ -48778,17 +49270,18 @@ class phone_joinGroupCall final : public Function { int32 flags_; bool muted_; bool video_stopped_; - object_ptr call_; + object_ptr call_; object_ptr join_as_; string invite_hash_; - int64 key_fingerprint_; + UInt256 public_key_; + bytes block_; object_ptr params_; - enum Flags : std::int32_t { MUTED_MASK = 1, VIDEO_STOPPED_MASK = 4, INVITE_HASH_MASK = 2, KEY_FINGERPRINT_MASK = 8 }; + enum Flags : std::int32_t { INVITE_HASH_MASK = 2, PUBLIC_KEY_MASK = 8, BLOCK_MASK = 8 }; mutable int32 var0; - phone_joinGroupCall(int32 flags_, bool muted_, bool video_stopped_, object_ptr &&call_, object_ptr &&join_as_, string const &invite_hash_, int64 key_fingerprint_, object_ptr &¶ms_); + phone_joinGroupCall(int32 flags_, bool muted_, bool video_stopped_, object_ptr &&call_, object_ptr &&join_as_, string const &invite_hash_, UInt256 const &public_key_, bytes &&block_, object_ptr &¶ms_); - static const std::int32_t ID = -702669325; + static const std::int32_t ID = -1883951017; using ReturnType = object_ptr; @@ -48801,22 +49294,22 @@ class phone_joinGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + class Updates; class dataJSON; -class inputGroupCall; - class phone_joinGroupCallPresentation final : public Function { std::int32_t get_id() const final { return ID; } public: - object_ptr call_; + object_ptr call_; object_ptr params_; - phone_joinGroupCallPresentation(object_ptr &&call_, object_ptr &¶ms_); + phone_joinGroupCallPresentation(object_ptr &&call_, object_ptr &¶ms_); static const std::int32_t ID = -873829436; @@ -48831,9 +49324,9 @@ class phone_joinGroupCallPresentation final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_leaveGroupCall final : public Function { std::int32_t get_id() const final { @@ -48841,10 +49334,10 @@ class phone_leaveGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; int32 source_; - phone_leaveGroupCall(object_ptr &&call_, int32 source_); + phone_leaveGroupCall(object_ptr &&call_, int32 source_); static const std::int32_t ID = 1342404601; @@ -48859,9 +49352,9 @@ class phone_leaveGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_leaveGroupCallPresentation final : public Function { std::int32_t get_id() const final { @@ -48869,9 +49362,9 @@ class phone_leaveGroupCallPresentation final : public Function { } public: - object_ptr call_; + object_ptr call_; - explicit phone_leaveGroupCallPresentation(object_ptr &&call_); + explicit phone_leaveGroupCallPresentation(object_ptr &&call_); static const std::int32_t ID = 475058500; @@ -48913,8 +49406,6 @@ class phone_receivedCall final : public Function { class InputUser; -class inputGroupCall; - class phoneCallProtocol; class phone_phoneCall; @@ -48928,16 +49419,14 @@ class phone_requestCall final : public Function { int32 flags_; bool video_; object_ptr user_id_; - object_ptr conference_call_; int32 random_id_; bytes g_a_hash_; object_ptr protocol_; - enum Flags : std::int32_t { VIDEO_MASK = 1, CONFERENCE_CALL_MASK = 2 }; mutable int32 var0; - phone_requestCall(int32 flags_, bool video_, object_ptr &&user_id_, object_ptr &&conference_call_, int32 random_id_, bytes &&g_a_hash_, object_ptr &&protocol_); + phone_requestCall(int32 flags_, bool video_, object_ptr &&user_id_, int32 random_id_, bytes &&g_a_hash_, object_ptr &&protocol_); - static const std::int32_t ID = -1497079796; + static const std::int32_t ID = 1124046573; using ReturnType = object_ptr; @@ -49032,6 +49521,34 @@ class phone_saveDefaultGroupCallJoinAs final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; +class InputGroupCall; + +class Updates; + +class phone_sendConferenceCallBroadcast final : public Function { + std::int32_t get_id() const final { + return ID; + } + + public: + object_ptr call_; + bytes block_; + + phone_sendConferenceCallBroadcast(object_ptr &&call_, bytes &&block_); + + static const std::int32_t ID = -965732096; + + using ReturnType = object_ptr; + + void store(TlStorerCalcLength &s) const final; + + void store(TlStorerUnsafe &s) const final; + + void store(TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(TlBufferParser &p); +}; + class inputPhoneCall; class phone_sendSignalingData final : public Function { @@ -49073,7 +49590,6 @@ class phone_setCallRating final : public Function { object_ptr peer_; int32 rating_; string comment_; - enum Flags : std::int32_t { USER_INITIATIVE_MASK = 1 }; mutable int32 var0; phone_setCallRating(int32 flags_, bool user_initiative_, object_ptr &&peer_, int32 rating_, string const &comment_); @@ -49091,9 +49607,9 @@ class phone_setCallRating final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_startScheduledGroupCall final : public Function { std::int32_t get_id() const final { @@ -49101,9 +49617,9 @@ class phone_startScheduledGroupCall final : public Function { } public: - object_ptr call_; + object_ptr call_; - explicit phone_startScheduledGroupCall(object_ptr &&call_); + explicit phone_startScheduledGroupCall(object_ptr &&call_); static const std::int32_t ID = 1451287362; @@ -49118,9 +49634,9 @@ class phone_startScheduledGroupCall final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_toggleGroupCallRecord final : public Function { std::int32_t get_id() const final { @@ -49131,13 +49647,13 @@ class phone_toggleGroupCallRecord final : public Function { int32 flags_; bool start_; bool video_; - object_ptr call_; + object_ptr call_; string title_; bool video_portrait_; - enum Flags : std::int32_t { START_MASK = 1, VIDEO_MASK = 4, TITLE_MASK = 2, VIDEO_PORTRAIT_MASK = 4 }; + enum Flags : std::int32_t { TITLE_MASK = 2, VIDEO_PORTRAIT_MASK = 4 }; mutable int32 var0; - phone_toggleGroupCallRecord(int32 flags_, bool start_, bool video_, object_ptr &&call_, string const &title_, bool video_portrait_); + phone_toggleGroupCallRecord(int32 flags_, bool start_, bool video_, object_ptr &&call_, string const &title_, bool video_portrait_); static const std::int32_t ID = -248985848; @@ -49152,9 +49668,9 @@ class phone_toggleGroupCallRecord final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_toggleGroupCallSettings final : public Function { std::int32_t get_id() const final { @@ -49164,12 +49680,12 @@ class phone_toggleGroupCallSettings final : public Function { public: int32 flags_; bool reset_invite_hash_; - object_ptr call_; + object_ptr call_; bool join_muted_; - enum Flags : std::int32_t { RESET_INVITE_HASH_MASK = 2, JOIN_MUTED_MASK = 1 }; + enum Flags : std::int32_t { JOIN_MUTED_MASK = 1 }; mutable int32 var0; - phone_toggleGroupCallSettings(int32 flags_, bool reset_invite_hash_, object_ptr &&call_, bool join_muted_); + phone_toggleGroupCallSettings(int32 flags_, bool reset_invite_hash_, object_ptr &&call_, bool join_muted_); static const std::int32_t ID = 1958458429; @@ -49184,9 +49700,9 @@ class phone_toggleGroupCallSettings final : public Function { static ReturnType fetch_result(TlBufferParser &p); }; -class Updates; +class InputGroupCall; -class inputGroupCall; +class Updates; class phone_toggleGroupCallStartSubscription final : public Function { std::int32_t get_id() const final { @@ -49194,10 +49710,10 @@ class phone_toggleGroupCallStartSubscription final : public Function { } public: - object_ptr call_; + object_ptr call_; bool subscribed_; - phone_toggleGroupCallStartSubscription(object_ptr &&call_, bool subscribed_); + phone_toggleGroupCallStartSubscription(object_ptr &&call_, bool subscribed_); static const std::int32_t ID = 563885286; @@ -49283,7 +49799,7 @@ class photos_updateProfilePhoto final : public Function { bool fallback_; object_ptr bot_; object_ptr id_; - enum Flags : std::int32_t { FALLBACK_MASK = 1, BOT_MASK = 2 }; + enum Flags : std::int32_t { BOT_MASK = 2 }; mutable int32 var0; photos_updateProfilePhoto(int32 flags_, bool fallback_, object_ptr &&bot_, object_ptr &&id_); @@ -49323,7 +49839,7 @@ class photos_uploadContactProfilePhoto final : public Function { object_ptr video_; double video_start_ts_; object_ptr video_emoji_markup_; - enum Flags : std::int32_t { SUGGEST_MASK = 8, SAVE_MASK = 16, FILE_MASK = 1, VIDEO_MASK = 2, VIDEO_START_TS_MASK = 4, VIDEO_EMOJI_MARKUP_MASK = 32 }; + enum Flags : std::int32_t { FILE_MASK = 1, VIDEO_MASK = 2, VIDEO_START_TS_MASK = 4, VIDEO_EMOJI_MARKUP_MASK = 32 }; mutable int32 var0; photos_uploadContactProfilePhoto(int32 flags_, bool suggest_, bool save_, object_ptr &&user_id_, object_ptr &&file_, object_ptr &&video_, double video_start_ts_, object_ptr &&video_emoji_markup_); @@ -49362,7 +49878,7 @@ class photos_uploadProfilePhoto final : public Function { object_ptr video_; double video_start_ts_; object_ptr video_emoji_markup_; - enum Flags : std::int32_t { FALLBACK_MASK = 8, BOT_MASK = 32, FILE_MASK = 1, VIDEO_MASK = 2, VIDEO_START_TS_MASK = 4, VIDEO_EMOJI_MARKUP_MASK = 16 }; + enum Flags : std::int32_t { BOT_MASK = 32, FILE_MASK = 1, VIDEO_MASK = 2, VIDEO_START_TS_MASK = 4, VIDEO_EMOJI_MARKUP_MASK = 16 }; mutable int32 var0; photos_uploadProfilePhoto(int32 flags_, bool fallback_, object_ptr &&bot_, object_ptr &&file_, object_ptr &&video_, double video_start_ts_, object_ptr &&video_emoji_markup_); @@ -49426,7 +49942,6 @@ class premium_getBoostsList final : public Function { object_ptr peer_; string offset_; int32 limit_; - enum Flags : std::int32_t { GIFTS_MASK = 1 }; mutable int32 var0; premium_getBoostsList(int32 flags_, bool gifts_, object_ptr &&peer_, string const &offset_, int32 limit_); @@ -49667,7 +50182,6 @@ class smsjobs_updateSettings final : public Function { public: int32 flags_; bool allow_international_; - enum Flags : std::int32_t { ALLOW_INTERNATIONAL_MASK = 1 }; mutable int32 var0; smsjobs_updateSettings(int32 flags_, bool allow_international_); @@ -49698,7 +50212,6 @@ class stats_getBroadcastRevenueStats final : public Function { int32 flags_; bool dark_; object_ptr peer_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; stats_getBroadcastRevenueStats(int32 flags_, bool dark_, object_ptr &&peer_); @@ -49788,7 +50301,6 @@ class stats_getBroadcastStats final : public Function { int32 flags_; bool dark_; object_ptr channel_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; stats_getBroadcastStats(int32 flags_, bool dark_, object_ptr &&channel_); @@ -49819,7 +50331,6 @@ class stats_getMegagroupStats final : public Function { int32 flags_; bool dark_; object_ptr channel_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; stats_getMegagroupStats(int32 flags_, bool dark_, object_ptr &&channel_); @@ -49881,7 +50392,6 @@ class stats_getMessageStats final : public Function { bool dark_; object_ptr channel_; int32 msg_id_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; stats_getMessageStats(int32 flags_, bool dark_, object_ptr &&channel_, int32 msg_id_); @@ -49943,7 +50453,6 @@ class stats_getStoryStats final : public Function { bool dark_; object_ptr peer_; int32 id_; - enum Flags : std::int32_t { DARK_MASK = 1 }; mutable int32 var0; stats_getStoryStats(int32 flags_, bool dark_, object_ptr &&peer_, int32 id_); @@ -50130,7 +50639,7 @@ class stickers_createStickerSet final : public Function { object_ptr thumb_; array> stickers_; string software_; - enum Flags : std::int32_t { MASKS_MASK = 1, EMOJIS_MASK = 32, TEXT_COLOR_MASK = 64, THUMB_MASK = 4, SOFTWARE_MASK = 8 }; + enum Flags : std::int32_t { THUMB_MASK = 4, SOFTWARE_MASK = 8 }; mutable int32 var0; stickers_createStickerSet(int32 flags_, bool masks_, bool emojis_, bool text_color_, object_ptr &&user_id_, string const &title_, string const &short_name_, object_ptr &&thumb_, array> &&stickers_, string const &software_); @@ -50328,7 +50837,6 @@ class stories_activateStealthMode final : public Function { int32 flags_; bool past_; bool future_; - enum Flags : std::int32_t { PAST_MASK = 1, FUTURE_MASK = 2 }; mutable int32 var0; stories_activateStealthMode(int32 flags_, bool past_, bool future_); @@ -50348,6 +50856,8 @@ class stories_activateStealthMode final : public Function { class InputPeer; +class stories_canSendStoryCount; + class stories_canSendStory final : public Function { std::int32_t get_id() const final { return ID; @@ -50358,9 +50868,9 @@ class stories_canSendStory final : public Function { explicit stories_canSendStory(object_ptr &&peer_); - static const std::int32_t ID = -941629475; + static const std::int32_t ID = 820732912; - using ReturnType = bool; + using ReturnType = object_ptr; void store(TlStorerCalcLength &s) const final; @@ -50503,7 +51013,7 @@ class stories_getAllStories final : public Function { bool next_; bool hidden_; string state_; - enum Flags : std::int32_t { NEXT_MASK = 2, HIDDEN_MASK = 4, STATE_MASK = 1 }; + enum Flags : std::int32_t { STATE_MASK = 1 }; mutable int32 var0; stories_getAllStories(int32 flags_, bool next_, bool hidden_, string const &state_); @@ -50728,7 +51238,7 @@ class stories_getStoryReactionsList final : public Function { object_ptr reaction_; string offset_; int32 limit_; - enum Flags : std::int32_t { FORWARDS_FIRST_MASK = 4, REACTION_MASK = 1, OFFSET_MASK = 2 }; + enum Flags : std::int32_t { REACTION_MASK = 1, OFFSET_MASK = 2 }; mutable int32 var0; stories_getStoryReactionsList(int32 flags_, bool forwards_first_, object_ptr &&peer_, int32 id_, object_ptr &&reaction_, string const &offset_, int32 limit_); @@ -50765,7 +51275,7 @@ class stories_getStoryViewsList final : public Function { int32 id_; string offset_; int32 limit_; - enum Flags : std::int32_t { JUST_CONTACTS_MASK = 1, REACTIONS_FIRST_MASK = 4, FORWARDS_FIRST_MASK = 8, Q_MASK = 2 }; + enum Flags : std::int32_t { Q_MASK = 2 }; mutable int32 var0; stories_getStoryViewsList(int32 flags_, bool just_contacts_, bool reactions_first_, bool forwards_first_, object_ptr &&peer_, string const &q_, int32 id_, string const &offset_, int32 limit_); @@ -50918,7 +51428,6 @@ class stories_sendReaction final : public Function { object_ptr peer_; int32 story_id_; object_ptr reaction_; - enum Flags : std::int32_t { ADD_TO_RECENT_MASK = 1 }; mutable int32 var0; stories_sendReaction(int32 flags_, bool add_to_recent_, object_ptr &&peer_, int32 story_id_, object_ptr &&reaction_); @@ -50968,7 +51477,7 @@ class stories_sendStory final : public Function { int32 period_; object_ptr fwd_from_id_; int32 fwd_from_story_; - enum Flags : std::int32_t { PINNED_MASK = 4, NOFORWARDS_MASK = 16, FWD_MODIFIED_MASK = 128, MEDIA_AREAS_MASK = 32, CAPTION_MASK = 1, ENTITIES_MASK = 2, PERIOD_MASK = 8, FWD_FROM_ID_MASK = 64, FWD_FROM_STORY_MASK = 64 }; + enum Flags : std::int32_t { MEDIA_AREAS_MASK = 32, CAPTION_MASK = 1, ENTITIES_MASK = 2, PERIOD_MASK = 8, FWD_FROM_ID_MASK = 64, FWD_FROM_STORY_MASK = 64 }; mutable int32 var0; stories_sendStory(int32 flags_, bool pinned_, bool noforwards_, bool fwd_modified_, object_ptr &&peer_, object_ptr &&media_, array> &&media_areas_, string const &caption_, array> &&entities_, array> &&privacy_rules_, int64 random_id_, int32 period_, object_ptr &&fwd_from_id_, int32 fwd_from_story_); @@ -51150,7 +51659,6 @@ class updates_getChannelDifference final : public Function { object_ptr filter_; int32 pts_; int32 limit_; - enum Flags : std::int32_t { FORCE_MASK = 1 }; mutable int32 var0; updates_getChannelDifference(int32 flags_, bool force_, object_ptr &&channel_, object_ptr &&filter_, int32 pts_, int32 limit_); @@ -51292,7 +51800,6 @@ class upload_getFile final : public Function { object_ptr location_; int64 offset_; int32 limit_; - enum Flags : std::int32_t { PRECISE_MASK = 1, CDN_SUPPORTED_MASK = 2 }; mutable int32 var0; upload_getFile(int32 flags_, bool precise_, bool cdn_supported_, object_ptr &&location_, int64 offset_, int32 limit_); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/mtproto/AuthData.h b/third-party/td/TdBinding/SharedHeaders/td/td/mtproto/AuthData.h index d7458a6f4b..9b1fcf96ea 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/mtproto/AuthData.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/mtproto/AuthData.h @@ -67,16 +67,20 @@ class AuthData { void set_main_auth_key(AuthKey auth_key) { main_auth_key_ = std::move(auth_key); } + void break_main_auth_key() { main_auth_key_.break_key(); } + const AuthKey &get_main_auth_key() const { // CHECK(has_main_auth_key()); return main_auth_key_; } + bool has_main_auth_key() const { return !main_auth_key_.empty(); } + bool need_main_auth_key() const { return !has_main_auth_key(); } @@ -85,12 +89,15 @@ class AuthData { CHECK(!auth_key.empty()); tmp_auth_key_ = std::move(auth_key); } + const AuthKey &get_tmp_auth_key() const { return tmp_auth_key_; } + bool was_tmp_auth_key() const { return use_pfs() && !tmp_auth_key_.empty(); } + bool need_tmp_auth_key(double now, double refresh_margin) const { if (!use_pfs()) { return false; @@ -103,12 +110,15 @@ class AuthData { } return false; } + void drop_main_auth_key() { main_auth_key_ = AuthKey(); } + void drop_tmp_auth_key() { tmp_auth_key_ = AuthKey(); } + bool has_tmp_auth_key(double now) const { if (!use_pfs()) { return false; @@ -128,6 +138,7 @@ class AuthData { } return get_main_auth_key(); } + bool has_auth_key(double now) const { if (use_pfs()) { return has_tmp_auth_key(now); @@ -138,6 +149,7 @@ class AuthData { bool get_auth_flag() const { return main_auth_key_.auth_flag(); } + void set_auth_flag(bool auth_flag) { main_auth_key_.set_auth_flag(auth_flag); if (!auth_flag) { @@ -148,6 +160,7 @@ class AuthData { bool get_bind_flag() const { return !use_pfs() || tmp_auth_key_.auth_flag(); } + void on_bind() { CHECK(use_pfs()); tmp_auth_key_.set_auth_flag(true); @@ -184,6 +197,7 @@ class AuthData { void set_session_id(uint64 session_id) { session_id_ = session_id; } + uint64 get_session_id() const { CHECK(session_id_ != 0); return session_id_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/AuthManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/AuthManager.h index 5c38fe23f5..899d026e27 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/AuthManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/AuthManager.h @@ -90,7 +90,7 @@ class AuthManager final : public NetActor { tl_object_ptr get_current_authorization_state_object() const; private: - static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit + static constexpr size_t MAX_NAME_LENGTH = 64; // server-side limit enum class State : int32 { None, diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BotRecommendationManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BotRecommendationManager.h index 814349c8d7..298a844bd5 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BotRecommendationManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BotRecommendationManager.h @@ -27,7 +27,7 @@ class BotRecommendationManager final : public Actor { void get_bot_recommendations(UserId bot_user_id, bool return_local, Promise> &&users_promise, - Promise> &&count_promise); + Promise &&count_promise); void open_bot_recommended_bot(UserId bot_user_id, UserId opened_bot_user_id, Promise &&promise); @@ -56,7 +56,7 @@ class BotRecommendationManager final : public Actor { void load_bot_recommendations(UserId bot_user_id, bool use_database, bool return_local, Promise> &&users_promise, - Promise> &&count_promise); + Promise &&count_promise); void fail_load_bot_recommendations_queries(UserId bot_user_id, Status &&error); @@ -74,8 +74,7 @@ class BotRecommendationManager final : public Actor { FlatHashMap bot_recommended_bots_; FlatHashMap>>, UserIdHash> get_bot_recommendations_queries_; - FlatHashMap>>, UserIdHash> - get_bot_recommendation_count_queries_[2]; + FlatHashMap>, UserIdHash> get_bot_recommendation_count_queries_[2]; }; } // namespace td diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BusinessConnectionManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BusinessConnectionManager.h index 093ad99bc1..0c34737697 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BusinessConnectionManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/BusinessConnectionManager.h @@ -137,7 +137,7 @@ class BusinessConnectionManager final : public Actor { void get_current_state(vector> &updates) const; private: - static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit for first/last name + static constexpr size_t MAX_NAME_LENGTH = 64; // server-side limit for first/last name struct BusinessConnection; struct PendingMessage; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallActor.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallActor.h index 3a9a2e250b..8dbfe1bea1 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallActor.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallActor.h @@ -10,8 +10,6 @@ #include "td/telegram/CallId.h" #include "td/telegram/DhConfig.h" #include "td/telegram/files/FileUploadId.h" -#include "td/telegram/GroupCallId.h" -#include "td/telegram/InputGroupCallId.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -92,6 +90,7 @@ struct CallState { vector emojis_fingerprint; string custom_parameters; bool allow_p2p{false}; + bool conference_supported{false}; Status error; @@ -102,8 +101,7 @@ class CallActor final : public NetQueryCallback { public: CallActor(Td *td, CallId call_id, ActorShared<> parent, Promise promise); - void create_call(UserId user_id, CallProtocol &&protocol, bool is_video, GroupCallId group_call_id, - Promise &&promise); + void create_call(UserId user_id, CallProtocol &&protocol, bool is_video, Promise &&promise); void accept_call(CallProtocol &&protocol, Promise promise); @@ -111,7 +109,8 @@ class CallActor final : public NetQueryCallback { void send_call_signaling_data(string &&data, Promise promise); - void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise promise); + void discard_call(bool is_disconnected, const string &invite_link, int32 duration, bool is_video, int64 connection_id, + Promise promise); void rate_call(int32 rating, string comment, vector> &&problems, Promise promise); @@ -120,8 +119,6 @@ class CallActor final : public NetQueryCallback { void send_call_log(td_api::object_ptr log_file, Promise promise); - void create_conference_call(Promise promise); - void update_call(tl_object_ptr call); private: @@ -155,7 +152,6 @@ class CallActor final : public NetQueryCallback { bool is_outgoing_{false}; bool is_video_{false}; UserId user_id_; - InputGroupCallId input_group_call_id_; CallId local_call_id_; int64 call_id_{0}; @@ -172,8 +168,6 @@ class CallActor final : public NetQueryCallback { void update_call_inner(tl_object_ptr call); - void update_conference_call(const telegram_api::object_ptr &conference_call); - tl_object_ptr get_input_phone_call(const char *source); bool load_dh_config(); void on_dh_config(Result> r_dh_config, bool dummy); @@ -223,8 +217,6 @@ class CallActor final : public NetQueryCallback { void on_save_log_query_result(FileUploadId file_upload_id, Promise promise, Result r_net_query); - void on_create_conference_call_result(Promise promise, Result r_net_query); - void on_get_call_config_result(Result r_net_query); void flush_call_state(); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallDiscardReason.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallDiscardReason.h index 329787a1bc..16d035aa6b 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallDiscardReason.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallDiscardReason.h @@ -14,9 +14,9 @@ namespace td { struct CallDiscardReason { - enum class Type : int32 { Empty, Missed, Disconnected, HungUp, Declined }; + enum class Type : int32 { Empty, Missed, Disconnected, HungUp, Declined, UpgradeToGroupCall }; Type type_ = Type::Empty; - string encrypted_key_; + string slug_; }; CallDiscardReason get_call_discard_reason(const telegram_api::object_ptr &reason); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallManager.h index e0fd07701c..af7d0accc0 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CallManager.h @@ -8,7 +8,6 @@ #include "td/telegram/CallActor.h" #include "td/telegram/CallId.h" -#include "td/telegram/GroupCallId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -30,21 +29,18 @@ class CallManager final : public Actor { public: CallManager(Td *td, ActorShared<> parent); - CallId get_call_id(int64 call_id); - void update_call(telegram_api::object_ptr call); void update_call_signaling_data(int64 call_id, string data); - void create_call(UserId user_id, CallProtocol &&protocol, bool is_video, GroupCallId group_call_id, - Promise promise); + void create_call(UserId user_id, CallProtocol &&protocol, bool is_video, Promise promise); void accept_call(CallId call_id, CallProtocol &&protocol, Promise promise); void send_call_signaling_data(CallId call_id, string &&data, Promise promise); - void discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id, - Promise promise); + void discard_call(CallId call_id, bool is_disconnected, const string &invite_link, int32 duration, bool is_video, + int64 connection_id, Promise promise); void rate_call(CallId call_id, int32 rating, string comment, vector> &&problems, Promise promise); @@ -53,8 +49,6 @@ class CallManager final : public Actor { void send_call_log(CallId call_id, td_api::object_ptr log_file, Promise promise); - void create_conference_call(CallId call_id, Promise promise); - private: bool close_flag_ = false; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChannelRecommendationManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChannelRecommendationManager.h index 4a572d5954..0d892da5ed 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChannelRecommendationManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChannelRecommendationManager.h @@ -32,7 +32,7 @@ class ChannelRecommendationManager final : public Actor { void get_channel_recommendations(DialogId dialog_id, bool return_local, Promise> &&chats_promise, - Promise> &&count_promise); + Promise &&count_promise); void open_channel_recommended_channel(DialogId dialog_id, DialogId opened_dialog_id, Promise &&promise); @@ -77,7 +77,7 @@ class ChannelRecommendationManager final : public Actor { void load_channel_recommendations(ChannelId channel_id, bool use_database, bool return_local, Promise> &&chats_promise, - Promise> &&count_promise); + Promise &&count_promise); void fail_load_channel_recommendations_queries(ChannelId channel_id, Status &&error); @@ -94,8 +94,7 @@ class ChannelRecommendationManager final : public Actor { FlatHashMap channel_recommended_dialogs_; FlatHashMap>>, ChannelIdHash> get_channel_recommendations_queries_; - FlatHashMap>>, ChannelIdHash> - get_channel_recommendation_count_queries_[2]; + FlatHashMap>, ChannelIdHash> get_channel_recommendation_count_queries_[2]; RecommendedDialogs recommended_channels_; vector>> get_recommended_channels_queries_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChatManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChatManager.h index 74b1f989d6..44e7d33ce1 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChatManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ChatManager.h @@ -121,6 +121,8 @@ class ChatManager final : public Actor { bool get_channel_stories_hidden(ChannelId channel_id) const; + bool get_channel_autotranslation(ChannelId channel_id) const; + bool can_poll_channel_active_stories(ChannelId channel_id) const; bool can_use_premium_custom_emoji_in_channel(ChannelId channel_id) const; @@ -259,6 +261,9 @@ class ChatManager final : public Actor { void toggle_channel_can_have_sponsored_messages(ChannelId channel_id, bool can_have_sponsored_messages, Promise &&promise); + void toggle_channel_has_automatic_translation(ChannelId channel_id, bool has_automatic_translation, + Promise &&promise); + void toggle_channel_has_hidden_participants(ChannelId channel_id, bool has_hidden_participants, Promise &&promise); @@ -507,6 +512,7 @@ class ChatManager final : public Actor { bool join_to_send = false; bool join_request = false; bool stories_hidden = false; + bool autotranslation = false; bool is_megagroup = false; bool is_gigagroup = false; @@ -628,8 +634,8 @@ class ChatManager final : public Actor { class ChatLogEvent; class ChannelLogEvent; - static constexpr size_t MAX_TITLE_LENGTH = 128; // server side limit for chat title - static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat/channel description + static constexpr size_t MAX_TITLE_LENGTH = 128; // server-side limit for chat title + static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server-side limit for chat/channel description static constexpr int32 MAX_ACTIVE_STORY_ID_RELOAD_TIME = 3600; // some reasonable limit diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CommonDialogManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CommonDialogManager.h index 8dcd56fa37..e0b1405c5c 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CommonDialogManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/CommonDialogManager.h @@ -42,7 +42,7 @@ class CommonDialogManager final : public Actor { private: void tear_down() final; - static constexpr int32 MAX_GET_DIALOGS = 100; // server side limit + static constexpr int32 MAX_GET_DIALOGS = 100; // server-side limit struct CommonDialogs { vector dialog_ids; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ConfigManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ConfigManager.h index de1527a6e3..61aa91f278 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ConfigManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ConfigManager.h @@ -77,7 +77,7 @@ class ConfigManager final : public NetQueryCallback { private: struct AppConfig { - static constexpr int32 CURRENT_VERSION = 71; + static constexpr int32 CURRENT_VERSION = 77; int32 version_ = 0; int32 hash_ = 0; telegram_api::object_ptr config_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogInviteLinkManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogInviteLinkManager.h index f3a44f3997..0ef752b225 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogInviteLinkManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogInviteLinkManager.h @@ -87,7 +87,7 @@ class DialogInviteLinkManager final : public Actor { void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise &&promise); private: - static constexpr size_t MAX_INVITE_LINK_TITLE_LENGTH = 32; // server side limit + static constexpr size_t MAX_INVITE_LINK_TITLE_LENGTH = 32; // server-side limit void tear_down() final; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogManager.h index af14ecb53a..03f95d8ce7 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogManager.h @@ -252,7 +252,7 @@ class DialogManager final : public Actor { vector search_dialogs_on_server(const string &query, int32 limit, Promise &&promise); - void reload_voice_chat_on_search(const string &username); + void reload_video_chat_on_search(const string &username); void reget_peer_settings(DialogId dialog_id); @@ -377,7 +377,7 @@ class DialogManager final : public Actor { }; WaitFreeHashMap resolved_usernames_; WaitFreeHashMap inaccessible_resolved_usernames_; - FlatHashSet reload_voice_chat_on_search_usernames_; + FlatHashSet reload_video_chat_on_search_usernames_; FlatHashMap>> resolve_dialog_username_queries_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogParticipantManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogParticipantManager.h index ad8bc77c10..e731493438 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogParticipantManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/DialogParticipantManager.h @@ -162,7 +162,7 @@ class DialogParticipantManager final : public Actor { static constexpr int32 CHANNEL_PARTICIPANT_CACHE_TIME = 1800; // some reasonable limit - static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server side limit + static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server-side limit void tear_down() final; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ForumTopicManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ForumTopicManager.h index 7570376046..e83b5d7a1e 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ForumTopicManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/ForumTopicManager.h @@ -116,7 +116,7 @@ class ForumTopicManager final : public Actor { void on_topic_message_count_changed(DialogId dialog_id, MessageId top_thread_message_id, int diff); private: - static constexpr size_t MAX_FORUM_TOPIC_TITLE_LENGTH = 128; // server side limit for forum topic title + static constexpr size_t MAX_FORUM_TOPIC_TITLE_LENGTH = 128; // server-side limit for forum topic title struct Topic { unique_ptr info_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallJoinParameters.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallJoinParameters.h new file mode 100644 index 0000000000..563bda666c --- /dev/null +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallJoinParameters.h @@ -0,0 +1,30 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" + +#include "td/utils/common.h" +#include "td/utils/Status.h" + +namespace td { + +struct GroupCallJoinParameters { + string payload_; + int32 audio_source_ = 0; + bool is_muted_ = false; + bool is_my_video_enabled_ = false; + + static Result get_group_call_join_parameters( + td_api::object_ptr &¶meters, bool allow_empty); + + bool is_empty() const { + return payload_.empty(); + } +}; + +} // namespace td diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallManager.h index efce0b9aab..3e2cba9f95 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallManager.h @@ -11,7 +11,9 @@ #include "td/telegram/GroupCallId.h" #include "td/telegram/GroupCallParticipant.h" #include "td/telegram/GroupCallParticipantOrder.h" +#include "td/telegram/InputGroupCall.h" #include "td/telegram/InputGroupCallId.h" +#include "td/telegram/MessageFullId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -19,6 +21,8 @@ #include "td/actor/actor.h" #include "td/actor/MultiTimeout.h" +#include "td/e2e/e2e_api.h" + #include "td/utils/common.h" #include "td/utils/FlatHashMap.h" #include "td/utils/Promise.h" @@ -28,6 +32,7 @@ namespace td { +struct GroupCallJoinParameters; class Td; class GroupCallManager final : public Actor { @@ -51,10 +56,13 @@ class GroupCallManager final : public Actor { void set_group_call_default_join_as(DialogId dialog_id, DialogId as_dialog_id, Promise &&promise); - void create_voice_chat(DialogId dialog_id, string title, int32 start_date, bool is_rtmp_stream, + void create_video_chat(DialogId dialog_id, string title, int32 start_date, bool is_rtmp_stream, Promise &&promise); - void get_voice_chat_rtmp_stream_url(DialogId dialog_id, bool revoke, + void create_group_call(td_api::object_ptr &&join_parameters, + Promise> &&promise); + + void get_video_chat_rtmp_stream_url(DialogId dialog_id, bool revoke, Promise> &&promise); void get_group_call(GroupCallId group_call_id, Promise> &&promise); @@ -65,7 +73,7 @@ class GroupCallManager final : public Actor { Promise> &&promise); void get_group_call_streams(GroupCallId group_call_id, - Promise> &&promise); + Promise> &&promise); void get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, int32 channel_id, td_api::object_ptr quality, @@ -73,10 +81,22 @@ class GroupCallManager final : public Actor { void start_scheduled_group_call(GroupCallId group_call_id, Promise &&promise); - void join_group_call(GroupCallId group_call_id, DialogId as_dialog_id, int32 audio_source, string &&payload, - bool is_muted, bool is_my_video_enabled, const string &invite_hash, int64 key_fingerprint, + void join_group_call(td_api::object_ptr &&api_input_group_call, + td_api::object_ptr &&join_parameters, + Promise> &&promise); + + void join_video_chat(GroupCallId group_call_id, DialogId as_dialog_id, + td_api::object_ptr &&join_parameters, const string &invite_hash, Promise &&promise); + void encrypt_group_call_data(GroupCallId group_call_id, + td_api::object_ptr &&data_channel, string &&data, + int32 unencrypted_prefix_size, Promise &&promise); + + void decrypt_group_call_data(GroupCallId group_call_id, DialogId participant_dialog_id, + td_api::object_ptr &&data_channel, string &&data, + Promise &&promise); + void start_group_call_screen_sharing(GroupCallId group_call_id, int32 audio_source, string &&payload, Promise &&promise); @@ -100,6 +120,17 @@ class GroupCallManager final : public Actor { void revoke_group_call_invite_link(GroupCallId group_call_id, Promise &&promise); + void invite_group_call_participant(GroupCallId group_call_id, UserId user_id, bool is_video, + Promise> &&promise); + + void decline_group_call_invitation(MessageFullId message_full_id, Promise &&promise); + + void delete_group_call_participants(GroupCallId group_call_id, const vector &user_ids, bool is_ban, + Promise &&promise); + + void do_delete_group_call_participants(InputGroupCallId input_group_call_id, vector user_ids, bool is_ban, + Promise &&promise); + void invite_group_call_participants(GroupCallId group_call_id, vector &&user_ids, Promise &&promise); void get_group_call_invite_link(GroupCallId group_call_id, bool can_self_unmute, Promise &&promise); @@ -119,6 +150,9 @@ class GroupCallManager final : public Actor { void toggle_group_call_participant_is_hand_raised(GroupCallId group_call_id, DialogId dialog_id, bool is_hand_raised, Promise &&promise); + void get_group_call_participants(td_api::object_ptr input_group_call, int32 limit, + Promise> &&promise); + void load_group_call_participants(GroupCallId group_call_id, int32 limit, Promise &&promise); void leave_group_call(GroupCallId group_call_id, Promise &&promise); @@ -129,6 +163,9 @@ class GroupCallManager final : public Actor { void on_update_group_call_connection(string &&connection_params); + void on_update_group_call_chain_blocks(InputGroupCallId input_group_call_id, int32 sub_chain_id, + vector &&blocks, int32 next_offset); + void on_update_group_call(tl_object_ptr group_call_ptr, DialogId dialog_id); void on_user_speaking_in_group_call(GroupCallId group_call_id, DialogId dialog_id, bool is_muted_by_admin, int32 date, @@ -142,22 +179,29 @@ class GroupCallManager final : public Actor { vector> &&participants, int32 version, bool is_recursive = false); - void process_join_group_call_response(InputGroupCallId input_group_call_id, uint64 generation, + void process_join_video_chat_response(InputGroupCallId input_group_call_id, uint64 generation, tl_object_ptr &&updates, Promise &&promise); void process_join_group_call_presentation_response(InputGroupCallId input_group_call_id, uint64 generation, tl_object_ptr &&updates, Status status); + void register_group_call(MessageFullId message_full_id, const char *source); + + void unregister_group_call(MessageFullId message_full_id, const char *source); + private: struct GroupCall; struct GroupCallParticipants; struct GroupCallRecentSpeakers; struct PendingJoinRequest; + struct PendingJoinPresentationRequest; static constexpr int32 RECENT_SPEAKER_TIMEOUT = 60 * 60; static constexpr int32 UPDATE_GROUP_CALL_PARTICIPANT_ORDER_TIMEOUT = 10; static constexpr int32 CHECK_GROUP_CALL_IS_JOINED_TIMEOUT = 10; - static constexpr size_t MAX_TITLE_LENGTH = 64; // server side limit for group call/call record title length + static constexpr int32 GROUP_CALL_BLOCK_POLL_TIMEOUT = 10; + static constexpr size_t MAX_TITLE_LENGTH = 64; // server-side limit for group call/call record title length + static constexpr size_t BLOCK_POLL_COUNT = 100; void tear_down() final; @@ -182,6 +226,16 @@ class GroupCallManager final : public Actor { void on_sync_participants_timeout(GroupCallId group_call_id); + static void on_update_group_call_timeout_callback(void *group_call_manager_ptr, int64 call_id); + + void on_update_group_call_timeout(int64 call_id); + + static void on_poll_group_call_blocks_timeout_callback(void *group_call_manager_ptr, int64 call_id); + + void on_poll_group_call_blocks_timeout(int64 call_id); + + void on_update_group_call_message(int64 call_id); + GroupCallId get_next_group_call_id(InputGroupCallId input_group_call_id); GroupCall *add_group_call(InputGroupCallId input_group_call_id, DialogId dialog_id); @@ -193,20 +247,22 @@ class GroupCallManager final : public Actor { Status can_manage_group_calls(DialogId dialog_id) const; - bool can_manage_group_call(InputGroupCallId input_group_call_id) const; + bool can_manage_group_call(InputGroupCallId input_group_call_id, bool allow_owned) const; + + bool can_manage_group_call(const GroupCall *group_call, bool allow_owned) const; bool get_group_call_can_self_unmute(InputGroupCallId input_group_call_id) const; bool get_group_call_joined_date_asc(InputGroupCallId input_group_call_id) const; - void on_voice_chat_created(DialogId dialog_id, InputGroupCallId input_group_call_id, Promise &&promise); + void on_video_chat_created(DialogId dialog_id, InputGroupCallId input_group_call_id, Promise &&promise); void finish_get_group_call(InputGroupCallId input_group_call_id, Result> &&result); void finish_get_group_call_streams(InputGroupCallId input_group_call_id, int32 audio_source, - Result> &&result, - Promise> &&promise); + Result> &&result, + Promise> &&promise); void finish_get_group_call_stream_segment(InputGroupCallId input_group_call_id, int32 audio_source, Result &&result, Promise &&promise); @@ -214,6 +270,13 @@ class GroupCallManager final : public Actor { void finish_check_group_call_is_joined(InputGroupCallId input_group_call_id, int32 audio_source, Result &&result); + void sync_conference_call_participants(InputGroupCallId input_group_call_id, + vector &&blockchain_participant_ids); + + void on_sync_conference_call_participants(InputGroupCallId input_group_call_id, + vector &&blockchain_participant_ids, + vector &&server_participant_ids); + static const string &get_group_call_title(const GroupCall *group_call); static bool get_group_call_is_joined(const GroupCall *group_call); @@ -240,18 +303,26 @@ class GroupCallManager final : public Actor { bool need_group_call_participants(InputGroupCallId input_group_call_id) const; - static bool need_group_call_participants(const GroupCall *group_call); + bool need_group_call_participants(InputGroupCallId input_group_call_id, const GroupCall *group_call) const; bool process_pending_group_call_participant_updates(InputGroupCallId input_group_call_id); bool is_my_audio_source(InputGroupCallId input_group_call_id, const GroupCall *group_call, int32 audio_source) const; + void on_create_group_call(int32 random_id, Result> &&r_updates, + Promise> &&promise); + + void on_get_group_call_join_payload(InputGroupCallId input_group_call_id, string &&payload); + + void on_create_group_call_finished(InputGroupCallId input_group_call_id, bool is_join, + Promise> &&promise); + void sync_group_call_participants(InputGroupCallId input_group_call_id); void on_sync_group_call_participants(InputGroupCallId input_group_call_id, Result> &&result); - static GroupCallParticipantOrder get_real_participant_order(bool can_self_unmute, + static GroupCallParticipantOrder get_real_participant_order(bool my_can_self_unmute, const GroupCallParticipant &participant, const GroupCallParticipants *participants); @@ -262,12 +333,12 @@ class GroupCallManager final : public Actor { int32 version, const string &offset, bool is_load, bool is_sync); static bool update_group_call_participant_can_be_muted(bool can_manage, const GroupCallParticipants *participants, - GroupCallParticipant &participant); + GroupCallParticipant &participant, bool force_is_admin); void update_group_call_participants_can_be_muted(InputGroupCallId input_group_call_id, bool can_manage, - GroupCallParticipants *participants); + GroupCallParticipants *participants, bool force_is_admin); - void update_group_call_participants_order(InputGroupCallId input_group_call_id, bool can_self_unmute, + void update_group_call_participants_order(InputGroupCallId input_group_call_id, bool my_can_self_unmute, GroupCallParticipants *participants, const char *source); // returns participant_count_diff and video_participant_count_diff @@ -282,6 +353,24 @@ class GroupCallManager final : public Actor { void finish_load_group_call_administrators(InputGroupCallId input_group_call_id, Result &&result); + void try_join_group_call(InputGroupCall &&input_group_call, GroupCallJoinParameters &&join_parameters, + Promise> &&promise); + + void do_join_group_call(InputGroupCall &&input_group_call, GroupCallJoinParameters &&join_parameters, + telegram_api::object_ptr updates, + Promise> &&promise); + + void on_join_group_call(InputGroupCall &&input_group_call, GroupCallJoinParameters &&join_parameters, + const tde2e_api::PrivateKeyId &private_key_id, const tde2e_api::PublicKeyId &public_key_id, + Result> &&r_updates, + Promise> &&promise); + + void process_join_group_call_response(InputGroupCallId input_group_call_id, bool is_join, int32 audio_source, + const tde2e_api::PrivateKeyId &private_key_id, + const tde2e_api::PublicKeyId &public_key_id, + telegram_api::object_ptr &&updates, + Promise> &&promise); + int32 cancel_join_group_call_request(InputGroupCallId input_group_call_id, GroupCall *group_call); int32 cancel_join_group_call_presentation_request(InputGroupCallId input_group_call_id); @@ -375,6 +464,20 @@ class GroupCallManager final : public Actor { void update_group_call_dialog(const GroupCall *group_call, const char *source, bool force); + void on_call_state_updated(GroupCall *group_call, const char *source); + + void set_blockchain_participant_ids(GroupCall *group_call, vector participant_ids); + + static vector get_emojis_fingerprint(const GroupCall *group_call); + + void on_call_verification_state_updated(GroupCall *group_call); + + void send_outbound_group_call_blockchain_messages(GroupCall *group_call); + + void poll_group_call_blocks(GroupCall *group_call, int32 sub_chain_id); + + void on_poll_group_call_blocks(InputGroupCallId input_group_call_id, int32 sub_chain_id); + vector> get_recent_speakers(const GroupCall *group_call, bool for_update); @@ -404,8 +507,26 @@ class GroupCallManager final : public Actor { FlatHashMap, InputGroupCallIdHash> group_calls_; + struct BeingCreatedCall { + bool is_join_ = false; + tde2e_api::PrivateKeyId private_key_id_{}; + tde2e_api::PublicKeyId public_key_id_{}; + int32 audio_source_ = 0; + }; + FlatHashMap being_created_group_calls_; + FlatHashMap group_call_join_payloads_; + + struct BeingJoinedCallBlocks { + bool is_inited_[2]; + vector blocks_[2]; + int32 next_offset_[2]; + }; + FlatHashMap being_joined_call_blocks_; + string pending_group_call_join_params_; + FlatHashMap real_input_group_call_ids_; + FlatHashMap, InputGroupCallIdHash> group_call_participants_; FlatHashMap, DialogIdHash> participant_id_to_group_call_id_; @@ -415,10 +536,14 @@ class GroupCallManager final : public Actor { load_group_call_queries_; FlatHashMap, InputGroupCallIdHash> pending_join_requests_; - FlatHashMap, InputGroupCallIdHash> + FlatHashMap, InputGroupCallIdHash> pending_join_presentation_requests_; uint64 join_group_request_generation_ = 0; + FlatHashMap group_call_messages_; + FlatHashMap group_call_message_full_ids_; + int64 current_call_id_ = 0; + uint64 toggle_recording_generation_ = 0; uint64 toggle_is_muted_generation_ = 0; @@ -432,6 +557,8 @@ class GroupCallManager final : public Actor { MultiTimeout pending_send_speaking_action_timeout_{"PendingSendSpeakingActionTimeout"}; MultiTimeout recent_speaker_update_timeout_{"RecentSpeakerUpdateTimeout"}; MultiTimeout sync_participants_timeout_{"SyncParticipantsTimeout"}; + MultiTimeout update_group_call_timeout_{"UpdateGroupCallTimeout"}; + MultiTimeout poll_group_call_blocks_timeout_{"PollGroupCallBlocksTimeout"}; }; } // namespace td diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallParticipant.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallParticipant.h index 8e1e7aa11c..2464bd21b3 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallParticipant.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/GroupCallParticipant.h @@ -78,9 +78,9 @@ struct GroupCallParticipant { bool set_pending_is_muted(bool is_muted, bool can_manage, bool is_admin); - GroupCallParticipantOrder get_real_order(bool can_self_unmute, bool joined_date_asc) const; + GroupCallParticipantOrder get_real_order(bool my_can_self_unmute, bool joined_date_asc) const; - GroupCallParticipantOrder get_server_order(bool can_self_unmute, bool joined_date_asc) const; + GroupCallParticipantOrder get_server_order(bool my_can_self_unmute, bool joined_date_asc) const; bool is_valid() const { return dialog_id.is_valid(); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InlineQueriesManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InlineQueriesManager.h index a8734d9428..0911d20798 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InlineQueriesManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InlineQueriesManager.h @@ -95,7 +95,7 @@ class InlineQueriesManager final : public Actor { private: static constexpr size_t MAX_RECENT_INLINE_BOTS = 20; // some reasonable value - static constexpr int32 INLINE_QUERY_DELAY_MS = 400; // server side limit + static constexpr int32 INLINE_QUERY_DELAY_MS = 400; // server-side limit Result> get_input_bot_inline_result( td_api::object_ptr &&result, bool *is_gallery, bool *force_vertical) const; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCall.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCall.h new file mode 100644 index 0000000000..8af8ab0539 --- /dev/null +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCall.h @@ -0,0 +1,54 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/ServerMessageId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/HashTableUtils.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class Td; + +class InputGroupCall { + string slug_; + + ServerMessageId server_message_id_; + + public: + static Result get_input_group_call(Td *td, + td_api::object_ptr &&input_group_call); + + bool operator==(const InputGroupCall &other) const { + return slug_ == other.slug_ && server_message_id_ == other.server_message_id_; + } + + bool operator!=(const InputGroupCall &other) const { + return !(*this == other); + } + + uint32 get_hash() const { + return slug_.empty() ? server_message_id_.get() : Hash()(slug_); + } + + telegram_api::object_ptr get_input_group_call() const; + + friend StringBuilder &operator<<(StringBuilder &string_builder, InputGroupCall input_group_call); +}; + +struct InputGroupCallHash { + uint32 operator()(InputGroupCall input_group_call) const { + return input_group_call.get_hash(); + } +}; + +} // namespace td diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCallId.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCallId.h index a1be1617c0..1913bfe06e 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCallId.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/InputGroupCallId.h @@ -21,7 +21,7 @@ class InputGroupCallId { public: InputGroupCallId() = default; - explicit InputGroupCallId(const tl_object_ptr &input_group_call); + explicit InputGroupCallId(const telegram_api::object_ptr &input_group_call); InputGroupCallId(int64 group_call_id, int64 access_hash) : group_call_id(group_call_id), access_hash(access_hash) { } @@ -46,7 +46,7 @@ class InputGroupCallId { return Hash()(group_call_id); } - tl_object_ptr get_input_group_call() const; + telegram_api::object_ptr get_input_group_call() const; template void store(StorerT &storer) const { diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/LinkManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/LinkManager.h index f9222142c9..55d234da35 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/LinkManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/LinkManager.h @@ -96,6 +96,10 @@ class LinkManager final : public Actor { static string get_dialog_invite_link(Slice invite_hash, bool is_internal); + static string get_group_call_invite_link_slug(Slice invite_link); + + static string get_group_call_invite_link(Slice slug, bool is_internal); + static string get_instant_view_link_url(Slice link); static string get_instant_view_link_rhash(Slice link); @@ -140,6 +144,7 @@ class LinkManager final : public Actor { class InternalLinkDialogReferralProgram; class InternalLinkEditProfileSettings; class InternalLinkGame; + class InternalLinkGroupCall; class InternalLinkInstantView; class InternalLinkInvoice; class InternalLinkLanguage; @@ -147,6 +152,7 @@ class LinkManager final : public Actor { class InternalLinkMainWebApp; class InternalLinkMessage; class InternalLinkMessageDraft; + class InternalLinkMyStars; class InternalLinkPassportDataRequest; class InternalLinkPremiumFeatures; class InternalLinkPremiumGift; @@ -166,7 +172,7 @@ class LinkManager final : public Actor { class InternalLinkUpgradedGift; class InternalLinkUserPhoneNumber; class InternalLinkUserToken; - class InternalLinkVoiceChat; + class InternalLinkVideoChat; class InternalLinkWebApp; enum class LinkType : int32 { External, TMe, Tg, Telegraph }; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContent.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContent.h index fe3ac2e038..146ed6e8d7 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContent.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContent.h @@ -326,6 +326,8 @@ bool update_message_content_extended_media( bool need_poll_message_content_extended_media(const MessageContent *content); +bool need_poll_conference_call_message_content(const MessageContent *content); + void set_message_content_video_start_timestamp(MessageContent *content, int32 start_timestamp); void get_message_content_animated_emoji_click_sticker(const MessageContent *content, MessageFullId message_full_id, diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContentType.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContentType.h index 809a0d59bb..1aaa56fe86 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContentType.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessageContentType.h @@ -88,7 +88,8 @@ enum class MessageContentType : int32 { StarGift, StarGiftUnique, PaidMessagesRefunded, - PaidMessagesPrice + PaidMessagesPrice, + ConferenceCall }; // increase MessageUnsupported::CURRENT_VERSION each time a new message content type is added diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessagesManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessagesManager.h index 8b13dd1837..7b57b2017d 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessagesManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/MessagesManager.h @@ -126,14 +126,15 @@ class MessagesManager final : public Actor { static constexpr int32 SEND_MESSAGE_FLAG_DISABLE_WEB_PAGE_PREVIEW = 1 << 1; static constexpr int32 SEND_MESSAGE_FLAG_HAS_REPLY_MARKUP = 1 << 2; static constexpr int32 SEND_MESSAGE_FLAG_HAS_ENTITIES = 1 << 3; - // static constexpr int32 SEND_MESSAGE_FLAG_IS_POST = 1 << 4; static constexpr int32 SEND_MESSAGE_FLAG_DISABLE_NOTIFICATION = 1 << 5; static constexpr int32 SEND_MESSAGE_FLAG_FROM_BACKGROUND = 1 << 6; static constexpr int32 SEND_MESSAGE_FLAG_CLEAR_DRAFT = 1 << 7; static constexpr int32 SEND_MESSAGE_FLAG_WITH_MY_SCORE = 1 << 8; static constexpr int32 SEND_MESSAGE_FLAG_IS_FROM_THREAD = 1 << 9; static constexpr int32 SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE = 1 << 10; - static constexpr int32 SEND_MESSAGE_FLAG_HAS_MESSAGE = 1 << 11; + static constexpr int32 SEND_MESSAGE_FLAG_HIDE_VIA = 1 << 11; + static constexpr int32 SEND_MESSAGE_FLAG_DROP_AUTHOR = 1 << 11; + static constexpr int32 SEND_MESSAGE_FLAG_DROP_MEDIA_CAPTIONS = 1 << 12; static constexpr int32 SEND_MESSAGE_FLAG_HAS_SEND_AS = 1 << 13; static constexpr int32 SEND_MESSAGE_FLAG_NOFORWARDS = 1 << 14; static constexpr int32 SEND_MESSAGE_FLAG_UPDATE_STICKER_SETS_ORDER = 1 << 15; @@ -246,9 +247,9 @@ class MessagesManager final : public Actor { int32 total_count, vector> &&messages, Promise &&promise); - bool on_update_message_id(int64 random_id, MessageId new_message_id, const char *source); + void wait_message_add(MessageFullId message_full_id, Promise &&promise); - void on_update_message_video_published(MessageFullId message_full_id); + bool on_update_message_id(int64 random_id, MessageId new_message_id, const char *source); void on_update_dialog_draft_message(DialogId dialog_id, MessageId top_thread_message_id, tl_object_ptr &&draft_message, bool force = false); @@ -920,6 +921,8 @@ class MessagesManager final : public Actor { int64 get_message_random_id(MessageFullId message_full_id); + bool need_poll_group_call_message(MessageFullId message_full_id); + void finish_gift_upgrade(MessageFullId message_full_id, Promise> &&promise); @@ -931,6 +934,8 @@ class MessagesManager final : public Actor { }; Result get_invoice_message_info(MessageFullId message_full_id); + Result get_group_call_message_id(MessageFullId message_full_id); + Result get_payment_successful_message_id(MessageFullId message_full_id); Result get_giveaway_message_id(MessageFullId message_full_id); @@ -1556,13 +1561,13 @@ class MessagesManager final : public Actor { class SetDialogFolderIdOnServerLogEvent; class UpdateDialogNotificationSettingsOnServerLogEvent; - static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server side limit - static constexpr int32 MAX_GET_DIALOGS = 100; // server side limit - static constexpr int32 MAX_GET_HISTORY = 100; // server side limit - static constexpr int32 MAX_SEARCH_MESSAGES = 100; // server side limit + static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server-side limit + static constexpr int32 MAX_GET_DIALOGS = 100; // server-side limit + static constexpr int32 MAX_GET_HISTORY = 100; // server-side limit + static constexpr int32 MAX_SEARCH_MESSAGES = 100; // server-side limit static constexpr int32 MIN_CHANNEL_DIFFERENCE = 1; static constexpr int32 MAX_CHANNEL_DIFFERENCE = 100; - static constexpr int32 MAX_BOT_CHANNEL_DIFFERENCE = 100000; // server side limit + static constexpr int32 MAX_BOT_CHANNEL_DIFFERENCE = 100000; // server-side limit static constexpr size_t MIN_DELETED_ASYNCHRONOUSLY_MESSAGES = 2; static constexpr size_t MAX_UNLOADED_MESSAGES = 5000; @@ -1994,8 +1999,12 @@ class MessagesManager final : public Actor { bool update_message_fact_check(const Dialog *d, Message *m, unique_ptr &&fact_check, bool need_save); + void on_unread_message_mention_removed(Dialog *d, const Message *m, const char *source); + bool update_message_contains_unread_mention(Dialog *d, Message *m, bool contains_unread_mention, const char *source); + void on_unread_message_reaction_removed(Dialog *d, const Message *m, const char *source); + bool remove_message_unread_reactions(Dialog *d, Message *m, const char *source); void read_message_content_from_updates(MessageId message_id, int32 read_date); @@ -2287,8 +2296,6 @@ class MessagesManager final : public Actor { void send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m, bool *need_update_dialog_pos); - void send_update_video_published(MessageFullId message_full_id); - void send_update_message_content(const Dialog *d, Message *m, bool is_message_in_dialog, const char *source); void send_update_message_content_impl(DialogId dialog_id, const Message *m, const char *source) const; @@ -3122,7 +3129,7 @@ class MessagesManager final : public Actor { update_scheduled_message_ids_; // new_message_id -> temporary_id FlatHashMap messages_to_restore_; // new_message_id -> temporary_id - FlatHashSet published_video_message_full_ids_; + FlatHashMap>, MessageFullIdHash> awaited_message_full_ids_; const char *debug_add_message_to_dialog_fail_reason_ = ""; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/OrderInfo.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/OrderInfo.h index 862d13c002..dd948d6113 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/OrderInfo.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/OrderInfo.h @@ -75,7 +75,7 @@ bool operator!=(const OrderInfo &lhs, const OrderInfo &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const OrderInfo &order_info); -unique_ptr get_order_info(tl_object_ptr order_info); +unique_ptr get_order_info(telegram_api::object_ptr order_info); tl_object_ptr get_order_info_object(const unique_ptr &order_info); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/PollManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/PollManager.h index cf0cb328e1..cf61cd6bde 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/PollManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/PollManager.h @@ -144,7 +144,7 @@ class PollManager final : public Actor { bool was_invalidated_ = false; // the list needs to be invalidated when voters are changed }; - static constexpr int32 MAX_GET_POLL_VOTERS = 50; // server side limit + static constexpr int32 MAX_GET_POLL_VOTERS = 50; // server-side limit static constexpr int32 UNLOAD_POLL_DELAY = 600; // some reasonable value class SetPollAnswerLogEvent; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/QuickReplyManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/QuickReplyManager.h index 799db49633..3b9673a537 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/QuickReplyManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/QuickReplyManager.h @@ -110,7 +110,7 @@ class QuickReplyManager final : public Actor { void get_current_state(vector> &updates) const; private: - static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server side limit + static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server-side limit struct QuickReplyMessage { QuickReplyMessage() = default; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Requests.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Requests.h index ab24df836d..bfd1fabdee 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Requests.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Requests.h @@ -94,8 +94,12 @@ class Requests { Promise create_text_request_promise(uint64 id); + Promise create_data_request_promise(uint64 id); + Promise create_http_url_request_promise(uint64 id); + Promise create_count_request_promise(uint64 id); + template void on_request(uint64 id, const T &) = delete; @@ -627,11 +631,11 @@ class Requests { void on_request(uint64 id, const td_api::getStory &request); - void on_request(uint64 id, const td_api::getChatsToSendStories &request); + void on_request(uint64 id, const td_api::getChatsToPostStories &request); - void on_request(uint64 id, const td_api::canSendStory &request); + void on_request(uint64 id, const td_api::canPostStory &request); - void on_request(uint64 id, td_api::sendStory &request); + void on_request(uint64 id, td_api::postStory &request); void on_request(uint64 id, td_api::editStory &request); @@ -711,7 +715,7 @@ class Requests { void on_request(uint64 id, td_api::sendCallSignalingData &request); - void on_request(uint64 id, const td_api::discardCall &request); + void on_request(uint64 id, td_api::discardCall &request); void on_request(uint64 id, td_api::sendCallRating &request); @@ -725,7 +729,7 @@ class Requests { void on_request(uint64 id, td_api::createVideoChat &request); - void on_request(uint64 id, const td_api::createGroupCall &request); + void on_request(uint64 id, td_api::createGroupCall &request); void on_request(uint64 id, const td_api::getVideoChatRtmpUrl &request); @@ -733,25 +737,33 @@ class Requests { void on_request(uint64 id, const td_api::getGroupCall &request); - void on_request(uint64 id, const td_api::startScheduledGroupCall &request); + void on_request(uint64 id, const td_api::startScheduledVideoChat &request); - void on_request(uint64 id, const td_api::toggleGroupCallEnabledStartNotification &request); + void on_request(uint64 id, const td_api::toggleVideoChatEnabledStartNotification &request); void on_request(uint64 id, td_api::joinGroupCall &request); + void on_request(uint64 id, td_api::joinVideoChat &request); + void on_request(uint64 id, td_api::startGroupCallScreenSharing &request); void on_request(uint64 id, const td_api::endGroupCallScreenSharing &request); - void on_request(uint64 id, td_api::setGroupCallTitle &request); + void on_request(uint64 id, td_api::setVideoChatTitle &request); - void on_request(uint64 id, const td_api::toggleGroupCallMuteNewParticipants &request); + void on_request(uint64 id, const td_api::toggleVideoChatMuteNewParticipants &request); void on_request(uint64 id, const td_api::revokeGroupCallInviteLink &request); - void on_request(uint64 id, const td_api::inviteGroupCallParticipants &request); + void on_request(uint64 id, const td_api::inviteGroupCallParticipant &request); - void on_request(uint64 id, const td_api::getGroupCallInviteLink &request); + void on_request(uint64 id, const td_api::declineGroupCallInvitation &request); + + void on_request(uint64 id, const td_api::banGroupCallParticipants &request); + + void on_request(uint64 id, const td_api::inviteVideoChatParticipants &request); + + void on_request(uint64 id, const td_api::getVideoChatInviteLink &request); void on_request(uint64 id, td_api::startGroupCallRecording &request); @@ -771,15 +783,21 @@ class Requests { void on_request(uint64 id, const td_api::toggleGroupCallParticipantIsHandRaised &request); + void on_request(uint64 id, td_api::getGroupCallParticipants &request); + void on_request(uint64 id, const td_api::loadGroupCallParticipants &request); void on_request(uint64 id, const td_api::leaveGroupCall &request); void on_request(uint64 id, const td_api::endGroupCall &request); - void on_request(uint64 id, const td_api::getGroupCallStreams &request); + void on_request(uint64 id, const td_api::getVideoChatStreams &request); - void on_request(uint64 id, td_api::getGroupCallStreamSegment &request); + void on_request(uint64 id, td_api::getVideoChatStreamSegment &request); + + void on_request(uint64 id, td_api::encryptGroupCallData &request); + + void on_request(uint64 id, td_api::decryptGroupCallData &request); void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); @@ -1245,6 +1263,8 @@ class Requests { void on_request(uint64 id, const td_api::toggleSupergroupCanHaveSponsoredMessages &request); + void on_request(uint64 id, const td_api::toggleSupergroupHasAutomaticTranslation &request); + void on_request(uint64 id, const td_api::toggleSupergroupHasHiddenMembers &request); void on_request(uint64 id, const td_api::toggleSupergroupHasAggressiveAntiSpamEnabled &request); @@ -1451,7 +1471,7 @@ class Requests { void on_request(uint64 id, td_api::stopPoll &request); - void on_request(uint64 id, const td_api::hideSuggestedAction &request); + void on_request(uint64 id, td_api::hideSuggestedAction &request); void on_request(uint64 id, const td_api::hideContactCloseBirthdays &request); @@ -1539,6 +1559,8 @@ class Requests { void on_request(uint64 id, td_api::transferGift &request); + void on_request(uint64 id, td_api::sendResoldGift &request); + void on_request(uint64 id, td_api::getReceivedGifts &request); void on_request(uint64 id, const td_api::getReceivedGift &request); @@ -1547,6 +1569,10 @@ class Requests { void on_request(uint64 id, const td_api::getUpgradedGiftWithdrawalUrl &request); + void on_request(uint64 id, const td_api::setGiftResalePrice &request); + + void on_request(uint64 id, td_api::searchGiftsForResale &request); + void on_request(uint64 id, td_api::createInvoiceLink &request); void on_request(uint64 id, td_api::refundStarPayment &request); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SavedMessagesManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SavedMessagesManager.h index 78bce7e8d1..c9a4b26f88 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SavedMessagesManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SavedMessagesManager.h @@ -74,7 +74,7 @@ class SavedMessagesManager final : public Actor { void get_current_state(vector> &updates) const; private: - static constexpr int32 MAX_GET_HISTORY = 100; // server side limit + static constexpr int32 MAX_GET_HISTORY = 100; // server-side limit static constexpr int64 MIN_PINNED_TOPIC_ORDER = static_cast(2147000000) << 32; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SendCodeHelper.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SendCodeHelper.h index 600068ba52..c0e9b44374 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SendCodeHelper.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SendCodeHelper.h @@ -47,15 +47,17 @@ class SendCodeHelper { telegram_api::object_ptr get_email_verify_purpose_login_setup() const; - Slice phone_number() const { + const string &get_phone_number() const { return phone_number_; } - Slice phone_code_hash() const { + + const string &get_phone_code_hash() const { return phone_code_hash_; } template void store(StorerT &storer) const; + template void parse(ParserT &parser); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGift.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGift.h index c4b235655e..ed6b65c2e2 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGift.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGift.h @@ -47,6 +47,7 @@ class StarGift { int32 num_ = 0; int32 unique_availability_issued_ = 0; int32 unique_availability_total_ = 0; + int64 resale_star_count_ = 0; friend bool operator==(const StarGift &lhs, const StarGift &rhs); @@ -84,6 +85,8 @@ class StarGift { td_api::object_ptr get_upgraded_gift_object(Td *td) const; + td_api::object_ptr get_gift_for_resale_object(Td *td) const; + td_api::object_ptr get_sent_gift_object(Td *td) const; void add_dependencies(Dependencies &dependencies) const; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttribute.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttribute.h index 5b498b6d22..c19ec0f104 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttribute.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttribute.h @@ -9,6 +9,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/MessageEntity.h" +#include "td/telegram/StarGiftAttributeId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -42,6 +43,8 @@ class StarGiftAttributeSticker { td_api::object_ptr get_upgraded_gift_symbol_object(const Td *td) const; + StarGiftAttributeId get_id(Td *td, bool is_model) const; + template void store(StorerT &storer) const; @@ -57,6 +60,7 @@ inline bool operator!=(const StarGiftAttributeSticker &lhs, const StarGiftAttrib class StarGiftAttributeBackdrop { string name_; + int32 id_ = 0; int32 center_color_ = 0; int32 edge_color_ = 0; int32 pattern_color_ = 0; @@ -81,6 +85,8 @@ class StarGiftAttributeBackdrop { td_api::object_ptr get_upgraded_gift_backdrop_object() const; + StarGiftAttributeId get_id() const; + template void store(StorerT &storer) const; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttributeId.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttributeId.h new file mode 100644 index 0000000000..610e679bac --- /dev/null +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftAttributeId.h @@ -0,0 +1,71 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/HashTableUtils.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class StarGiftAttributeId { + enum class Type : int32 { None, Model, Pattern, Backdrop }; + Type type_ = Type::None; + int64 sticker_id_ = 0; + int32 backdrop_id_ = 0; + + StarGiftAttributeId(Type type, int64 sticker_id, int32 backdrop_id); + + static Result get_star_gift_attribute_id( + const td_api::object_ptr &attribute); + + telegram_api::object_ptr get_input_star_gift_attribute_id_object() const; + + friend struct StarGiftAttributeIdHash; + + friend bool operator==(const StarGiftAttributeId &lhs, const StarGiftAttributeId &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftAttributeId &attribute_id); + + public: + StarGiftAttributeId() = default; + + explicit StarGiftAttributeId(telegram_api::object_ptr attribute); + + static StarGiftAttributeId model(int64 sticker_id); + + static StarGiftAttributeId pattern(int64 sticker_id); + + static StarGiftAttributeId backdrop(int32 backdrop_id); + + static Result> get_star_gift_attribute_ids( + const vector> &attributes); + + static vector> get_input_star_gift_attribute_ids_object( + const vector &attributes); +}; + +struct StarGiftAttributeIdHash { + uint32 operator()(StarGiftAttributeId star_gift_attribute_id) const { + return star_gift_attribute_id.backdrop_id_ ? Hash()(star_gift_attribute_id.backdrop_id_) + : Hash()(star_gift_attribute_id.sticker_id_); + } +}; + +bool operator==(const StarGiftAttributeId &lhs, const StarGiftAttributeId &rhs); + +inline bool operator!=(const StarGiftAttributeId &lhs, const StarGiftAttributeId &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftAttributeId &attribute_id); + +} // namespace td diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftId.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftId.h index 232be27b06..4cdcd0bd04 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftId.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftId.h @@ -18,7 +18,7 @@ namespace td { class Td; class StarGiftId { - enum class Type : int32 { Empty, ForUser, ForDialog }; + enum class Type : int32 { Empty, ForUser, ForDialog, Slug }; Type type_ = Type::Empty; ServerMessageId server_message_id_; @@ -26,6 +26,8 @@ class StarGiftId { DialogId dialog_id_; int64 saved_id_ = 0; + string slug_; + friend bool operator==(const StarGiftId &lhs, const StarGiftId &rhs); friend StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftId &star_gift_id); @@ -37,6 +39,8 @@ class StarGiftId { StarGiftId(DialogId dialog_id, int64 saved_id); + static StarGiftId from_slug(const string &slug); + explicit StarGiftId(const string &star_gift_id); bool is_empty() const { diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftManager.h index 2cd5464daa..4deb44ed05 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StarGiftManager.h @@ -38,7 +38,7 @@ class StarGiftManager final : public Actor { StarGiftManager &operator=(StarGiftManager &&) = delete; ~StarGiftManager() final; - void get_gift_payment_options(Promise> &&promise); + void get_gift_payment_options(Promise> &&promise); void on_get_star_gift(const StarGift &star_gift, bool from_server); @@ -61,6 +61,9 @@ class StarGiftManager final : public Actor { void transfer_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, DialogId receiver_dialog_id, int64 star_count, Promise &&promise); + void send_resold_gift(const string &gift_name, DialogId receiver_dialog_id, int64 star_count, + Promise &&promise); + void get_saved_star_gifts(BusinessConnectionId business_connection_id, DialogId dialog_id, bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, bool exclude_limited, bool exclude_unique, bool sort_by_value, const string &offset, int32 limit, @@ -72,6 +75,13 @@ class StarGiftManager final : public Actor { void get_star_gift_withdrawal_url(StarGiftId star_gift_id, const string &password, Promise &&promise); + void set_star_gift_price(StarGiftId star_gift_id, int64 resale_star_count, Promise &&promise); + + void get_resale_star_gifts(int64 gift_id, const td_api::object_ptr &order, + const vector> &attributes, + const string &offset, int32 limit, + Promise> &&promise); + void register_gift(MessageFullId message_full_id, const char *source); void unregister_gift(MessageFullId message_full_id, const char *source); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StickersManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StickersManager.h index f9314283f9..df837467d0 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StickersManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StickersManager.h @@ -75,6 +75,8 @@ class StickersManager final : public Actor { StickerFormat get_sticker_format(FileId file_id) const; + int64 get_sticker_id(FileId sticker_id) const; + bool is_premium_custom_emoji(CustomEmojiId custom_emoji_id, bool default_result) const; bool have_sticker(StickerSetId sticker_set_id, int64 sticker_id); @@ -464,9 +466,9 @@ class StickersManager final : public Actor { static constexpr int32 MAX_FEATURED_STICKER_SET_VIEW_DELAY = 5; static constexpr int32 OLD_FEATURED_STICKER_SET_SLICE_SIZE = 20; - static constexpr int32 MAX_FOUND_STICKERS = 100; // server side limit - static constexpr size_t MAX_STICKER_SET_TITLE_LENGTH = 64; // server side limit - static constexpr size_t MAX_STICKER_SET_SHORT_NAME_LENGTH = 64; // server side limit + static constexpr int32 MAX_FOUND_STICKERS = 100; // server-side limit + static constexpr size_t MAX_STICKER_SET_TITLE_LENGTH = 64; // server-side limit + static constexpr size_t MAX_STICKER_SET_SHORT_NAME_LENGTH = 64; // server-side limit static constexpr size_t MAX_GET_CUSTOM_EMOJI_STICKERS = 200; // server-side limit static constexpr int32 EMOJI_KEYWORDS_UPDATE_DELAY = 3600; @@ -607,8 +609,6 @@ class StickersManager final : public Actor { class UploadStickerFileCallback; - int64 get_sticker_id(FileId sticker_id) const; - CustomEmojiId get_custom_emoji_id(FileId sticker_id) const; PhotoFormat get_sticker_set_thumbnail_format(const StickerSet *sticker_set) const; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StoryManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StoryManager.h index 7f8a770e55..8ff8bb880f 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StoryManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/StoryManager.h @@ -220,7 +220,7 @@ class StoryManager final : public Actor { void update_dialogs_to_send_stories(ChannelId channel_id, bool can_send_stories); - void can_send_story(DialogId dialog_id, Promise> &&promise); + void can_send_story(DialogId dialog_id, Promise> &&promise); void send_story(DialogId dialog_id, td_api::object_ptr &&input_story_content, td_api::object_ptr &&input_areas, @@ -373,7 +373,7 @@ class StoryManager final : public Actor { td_api::object_ptr get_stories_object(int32 total_count, const vector &story_full_ids, const vector &pinned_story_ids) const; - static td_api::object_ptr get_can_send_story_result_object(const Status &error, + static td_api::object_ptr get_can_post_story_result_object(const Status &error, bool force = false); FileSourceId get_story_file_source_id(StoryFullId story_full_id); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SuggestedAction.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SuggestedAction.h index ef69243d8a..ae882701c3 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SuggestedAction.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/SuggestedAction.h @@ -7,7 +7,9 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/MessageEntity.h" #include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/HashTableUtils.h" @@ -16,6 +18,8 @@ namespace td { +class UserManager; + struct SuggestedAction { enum class Type : int32 { Empty, @@ -32,11 +36,16 @@ struct SuggestedAction { BirthdaySetup, PremiumGrace, StarsSubscriptionLowBalance, - UserpicSetup + UserpicSetup, + Custom }; Type type_ = Type::Empty; DialogId dialog_id_; int32 otherwise_relogin_days_ = 0; + string custom_type_; + FormattedText title_; + FormattedText description_; + string url_; void init(Type type); @@ -48,9 +57,11 @@ struct SuggestedAction { explicit SuggestedAction(Slice action_str); + SuggestedAction(const UserManager *user_manager, telegram_api::object_ptr action); + SuggestedAction(Slice action_str, DialogId dialog_id); - explicit SuggestedAction(const td_api::object_ptr &suggested_action); + explicit SuggestedAction(td_api::object_ptr &&suggested_action); bool is_empty() const { return type_ == Type::Empty; @@ -58,7 +69,7 @@ struct SuggestedAction { string get_suggested_action_str() const; - td_api::object_ptr get_suggested_action_object() const; + td_api::object_ptr get_suggested_action_object(const UserManager *user_manager) const; template void store(StorerT &storer) const; @@ -74,7 +85,8 @@ struct SuggestedActionHash { }; inline bool operator==(const SuggestedAction &lhs, const SuggestedAction &rhs) { - return lhs.type_ == rhs.type_ && lhs.dialog_id_ == rhs.dialog_id_; + return lhs.type_ == rhs.type_ && lhs.dialog_id_ == rhs.dialog_id_ && lhs.custom_type_ == rhs.custom_type_ && + lhs.url_ == rhs.url_; } inline bool operator!=(const SuggestedAction &lhs, const SuggestedAction &rhs) { @@ -85,16 +97,24 @@ inline bool operator<(const SuggestedAction &lhs, const SuggestedAction &rhs) { if (lhs.dialog_id_ != rhs.dialog_id_) { return lhs.dialog_id_.get() < rhs.dialog_id_.get(); } - return static_cast(lhs.type_) < static_cast(rhs.type_); + if (lhs.type_ != rhs.type_) { + return static_cast(lhs.type_) < static_cast(rhs.type_); + } + if (lhs.custom_type_ != rhs.custom_type_) { + return lhs.custom_type_ == rhs.custom_type_; + } + return lhs.url_ < rhs.url_; } td_api::object_ptr get_update_suggested_actions_object( - const vector &added_actions, const vector &removed_actions, const char *source); + const UserManager *user_manager, const vector &added_actions, + const vector &removed_actions, const char *source); -bool update_suggested_actions(vector &suggested_actions, +bool update_suggested_actions(const UserManager *user_manager, vector &suggested_actions, vector &&new_suggested_actions); -bool remove_suggested_action(vector &suggested_actions, SuggestedAction suggested_action); +bool remove_suggested_action(const UserManager *user_manager, vector &suggested_actions, + SuggestedAction suggested_action); void dismiss_suggested_action(SuggestedAction action, Promise &&promise); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/TranslationManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/TranslationManager.h index 22ee519dc1..9e0bb01bc8 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/TranslationManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/TranslationManager.h @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/MessageEntity.h" +#include "td/telegram/MessageFullId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -27,7 +28,8 @@ class TranslationManager final : public Actor { Promise> &&promise); void translate_text(FormattedText text, bool skip_bot_commands, int32 max_media_timestamp, - const string &to_language_code, Promise> &&promise); + MessageFullId message_full_id, const string &to_language_code, + Promise> &&promise); private: void tear_down() final; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UpdatesManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UpdatesManager.h index f7a1ccae95..d6009b8c30 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UpdatesManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UpdatesManager.h @@ -115,7 +115,7 @@ class UpdatesManager final : public Actor { static vector> get_new_messages( const telegram_api::Updates *updates_ptr); - static vector get_update_new_group_call_ids(const telegram_api::Updates *updates_ptr); + static InputGroupCallId get_update_new_group_call_id(const telegram_api::Updates *updates_ptr); void process_updates_users_and_chats(telegram_api::Updates *updates_ptr); @@ -623,6 +623,7 @@ class UpdatesManager final : public Actor { void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserManager.h index 21fe919dd7..d0057f8e67 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserManager.h @@ -449,7 +449,7 @@ class UserManager final : public Actor { void on_deleted_contacts(const vector &deleted_contact_user_ids); - int32 get_imported_contact_count(Promise &&promise); + void get_imported_contact_count(Promise &&promise); std::pair, vector> change_imported_contacts(vector &contacts, int64 &random_id, Promise &&promise); @@ -752,8 +752,8 @@ class UserManager final : public Actor { class UserLogEvent; class SecretChatLogEvent; - static constexpr int32 MAX_GET_PROFILE_PHOTOS = 100; // server side limit - static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit for first/last name + static constexpr int32 MAX_GET_PROFILE_PHOTOS = 100; // server-side limit + static constexpr size_t MAX_NAME_LENGTH = 64; // server-side limit for first/last name static constexpr int32 MAX_ACTIVE_STORY_ID_RELOAD_TIME = 3600; // some reasonable limit diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserStarGift.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserStarGift.h index 591478bdac..097581988d 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserStarGift.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/UserStarGift.h @@ -28,6 +28,8 @@ class UserStarGift { int64 upgrade_star_count_ = 0; int64 transfer_star_count_ = 0; int32 date_ = 0; + int32 can_transfer_at_ = 0; + int32 can_resell_at_ = 0; int32 can_export_at_ = 0; bool is_name_hidden_ = false; bool is_saved_ = false; diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Version.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Version.h index a3df017eba..8f779a5cea 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Version.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/Version.h @@ -10,7 +10,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 201; +constexpr int32 MTPROTO_LAYER = 203; enum class Version : int32 { Initial, // 0 diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/files/FileManager.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/files/FileManager.h index efce5456ce..f605c71d56 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/files/FileManager.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/files/FileManager.h @@ -550,8 +550,7 @@ class FileManager final : public Actor { Result get_suggested_file_name(FileId file_id, const string &directory); - void read_file_part(FileId file_id, int64 offset, int64 count, int left_tries, - Promise> promise); + void read_file_part(FileId file_id, int64 offset, int64 count, int left_tries, Promise promise); void delete_file(FileId file_id, Promise promise, const char *source); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/logevent/LogEvent.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/logevent/LogEvent.h index 915d9266ed..92308d2490 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/logevent/LogEvent.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/logevent/LogEvent.h @@ -13,7 +13,6 @@ #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" -#include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StorerBase.h" @@ -229,8 +228,6 @@ BufferSlice log_event_store_impl(const T &data, const char *file, int line) { BufferSlice value_buffer{storer_calc_length.get_length()}; auto ptr = value_buffer.as_mutable_slice().ubegin(); - LOG_CHECK(is_aligned_pointer<4>(ptr)) << ptr; - LogEventStorerUnsafe storer_unsafe(ptr); store(data, storer_unsafe); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/misc.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/misc.h index bae968ac3e..66fcf6d966 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/misc.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/misc.h @@ -45,8 +45,8 @@ uint64 get_md5_string_hash(const string &str) TD_WARN_UNUSED_RESULT; // calculates hash of list of uint64 int64 get_vector_hash(const vector &numbers) TD_WARN_UNUSED_RESULT; -// returns emoji corresponding to the specified number -string get_emoji_fingerprint(uint64 num); +// returns 4 emoji corresponding to the specified 32-byte buffer +vector get_emoji_fingerprints(const unsigned char *buffer); // checks whether currency amount is valid bool check_currency_amount(int64 amount); diff --git a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/net/Session.h b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/net/Session.h index 9c1e9ccb2d..ff8cb407dc 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/td/telegram/net/Session.h +++ b/third-party/td/TdBinding/SharedHeaders/td/td/telegram/net/Session.h @@ -149,7 +149,7 @@ class Session final PendingQueries pending_queries_; std::map sent_queries_; std::deque pending_invoke_after_queries_; - ListNode sent_queries_list_; + ListNode sent_query_list_; struct ConnectionInfo { int8 connection_id_ = 0; @@ -197,7 +197,7 @@ class Session final double wakeup_at_ = 0.0; - // mtproto::AuthData should be the last field, because it's size is about 32 KB + // mtproto::AuthData should be the last field, because its size is about 32 KB mtproto::AuthData auth_data_; void on_handshake_ready(Result> r_handshake); diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler-decl.h b/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler-decl.h index f05aa794cf..ecdd0e48b2 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler-decl.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler-decl.h @@ -228,8 +228,8 @@ class Scheduler { unique_ptr> actor_info_pool_; int32 actor_count_ = 0; - ListNode pending_actors_list_; - ListNode ready_actors_list_; + ListNode pending_actors_; + ListNode ready_actors_; KHeap timeout_queue_; FlatHashMap> pending_events_; diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler.h b/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler.h index 8a23ca03da..e9ae2c5f08 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdactor/td/actor/impl/Scheduler.h @@ -111,7 +111,7 @@ ActorOwn Scheduler::register_actor_impl(Slice name, ActorT *actor_ptr, A send_later(actor_id, Event::start()); do_migrate_actor(actor_info, sched_id); } else { - pending_actors_list_.put(weak_info->get_list_node()); + pending_actors_.put(weak_info->get_list_node()); if (ActorTraits::need_start_up) { send_later(actor_id, Event::start()); } diff --git a/third-party/td/TdBinding/SharedHeaders/td/tddb/td/db/BinlogKeyValue.h b/third-party/td/TdBinding/SharedHeaders/td/tddb/td/db/BinlogKeyValue.h index c9c2a1ee1d..e713187738 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tddb/td/db/BinlogKeyValue.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tddb/td/db/BinlogKeyValue.h @@ -45,13 +45,13 @@ class BinlogKeyValue final : public KeyValueSyncInterface { Slice key; Slice value; template - void store(StorerT &&storer) const { + void store(StorerT &storer) const { storer.store_string(key); storer.store_string(value); } template - void parse(ParserT &&parser) { + void parse(ParserT &parser) { key = parser.template fetch_string(); value = parser.template fetch_string(); } @@ -84,7 +84,10 @@ class BinlogKeyValue final : public KeyValueSyncInterface { name, [&](const BinlogEvent &binlog_event) { Event event; - event.parse(TlParser(binlog_event.get_data())); + TlParser event_parser(binlog_event.get_data()); + event.parse(event_parser); + event_parser.fetch_end(); + LOG_CHECK(event_parser.get_error() == nullptr) << event_parser.get_status(); if (event.key.empty()) { LOG(ERROR) << "Have event with empty key"; return; @@ -109,7 +112,10 @@ class BinlogKeyValue final : public KeyValueSyncInterface { void external_init_handle(const BinlogEvent &binlog_event) { Event event; - event.parse(TlParser(binlog_event.get_data())); + TlParser event_parser(binlog_event.get_data()); + event.parse(event_parser); + event_parser.fetch_end(); + LOG_CHECK(event_parser.get_error() == nullptr) << event_parser.get_status(); if (event.key.empty()) { LOG(ERROR) << "Have external event with empty key"; return; diff --git a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Blockchain.h b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Blockchain.h index fad35e91ef..59d48e34c9 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Blockchain.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Blockchain.h @@ -43,7 +43,7 @@ struct GroupParticipant { td::int64 user_id{0}; td::int32 flags{0}; PublicKey public_key{}; - td::int32 version{0}; + td::int32 version{1}; bool add_users() const { return (flags & GroupParticipantFlags::AddUsers) != 0; } diff --git a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Mnemonic.h b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Mnemonic.h index 79534ee0d7..2869e1b255 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Mnemonic.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/Mnemonic.h @@ -23,7 +23,7 @@ class Mnemonic { static td::Result create(std::vector words, td::SecureString password); struct Options { Options(); - int words_count = 24; + int word_count = 24; td::SecureString password; td::SecureString entropy; }; diff --git a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_api.h b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_api.h index 7f70dd35c1..7c6dcad802 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_api.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_api.h @@ -54,6 +54,11 @@ class Result { return std::holds_alternative(data_); } + // Check if the result is an error + bool is_error() const { + return std::holds_alternative(data_); + } + T &value() { return std::get(data_); } @@ -283,8 +288,8 @@ Result storage_get_blockchain_state(StorageId); using CallId = std::int64_t; using CallChannelId = std::int32_t; struct CallParticipant { - UserId user_id; - PublicKeyId public_key_id; + UserId user_id{}; + PublicKeyId public_key_id{}; int permissions{}; }; diff --git a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_errors.h b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_errors.h index 66735716ec..00c7bc970f 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_errors.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tde2e/td/e2e/e2e_errors.h @@ -41,6 +41,7 @@ enum class ErrorCode : int { CallFailed = 601, CallKeyAlreadyUsed = 602 }; + inline std::string_view error_string(ErrorCode error_code) { switch (error_code) { case ErrorCode::Any: diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdtl/td/tl/tl_config.h b/third-party/td/TdBinding/SharedHeaders/td/tdtl/td/tl/tl_config.h index 0b3e55fde0..353ff1bcb6 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdtl/td/tl/tl_config.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdtl/td/tl/tl_config.h @@ -64,7 +64,7 @@ class tl_config_parser { tl_tree *read_type_expr(int *var_count); tl_tree *read_nat_expr(int *var_count); tl_tree *read_expr(int *var_count); - std::vector read_args_list(int *var_count); + std::vector read_args(int *var_count); tl_combinator *read_combinator(); tl_type *read_type(); diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/StealingQueue.h b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/StealingQueue.h index 092dbf8017..c3ab53ba3c 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/StealingQueue.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/StealingQueue.h @@ -21,7 +21,7 @@ class StealingQueue { // tries to put a value // returns if succeeded - // only owner is allowed to to do this + // only owner is allowed to do this template void local_push(T value, F &&overflow_f) { while (true) { diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/emoji.h b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/emoji.h index a9877d581e..cb9af3fcf8 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/emoji.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/emoji.h @@ -14,7 +14,7 @@ namespace td { // checks whether the string is an emoji; variation selectors are ignored bool is_emoji(Slice str); -// checks whether emoji ends on a Fitzpatrick modifier and returns it's number or 0 +// checks whether emoji ends on a Fitzpatrick modifier and returns its number or 0 int get_fitzpatrick_modifier(Slice emoji); // removes all Fitzpatrick modifier from the end of the string diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_helpers.h b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_helpers.h index 8deea3e774..7274455a21 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_helpers.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_helpers.h @@ -12,7 +12,6 @@ #include "td/utils/SharedSlice.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" -#include "td/utils/StackAllocator.h" #include "td/utils/Status.h" #include "td/utils/tl_parsers.h" #include "td/utils/tl_storers.h" @@ -287,19 +286,10 @@ string serialize(const T &object) { size_t length = calc_length.get_length(); string key(length, '\0'); - if (!is_aligned_pointer<4>(key.data())) { - auto ptr = StackAllocator::alloc(length); - MutableSlice data = ptr.as_slice(); - TlStorerUnsafe storer(data.ubegin()); - store(object, storer); - CHECK(storer.get_buf() == data.uend()); - key.assign(data.begin(), data.size()); - } else { - MutableSlice data = key; - TlStorerUnsafe storer(data.ubegin()); - store(object, storer); - CHECK(storer.get_buf() == data.uend()); - } + MutableSlice data = key; + TlStorerUnsafe storer(data.ubegin()); + store(object, storer); + CHECK(storer.get_buf() == data.uend()); return key; } @@ -310,7 +300,6 @@ SecureString serialize_secure(const T &object) { size_t length = calc_length.get_length(); SecureString key(length, '\0'); - CHECK(is_aligned_pointer<4>(key.data())); MutableSlice data = key.as_mutable_slice(); TlStorerUnsafe storer(data.ubegin()); store(object, storer); diff --git a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_parsers.h b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_parsers.h index 62576124d2..571ff7a119 100644 --- a/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_parsers.h +++ b/third-party/td/TdBinding/SharedHeaders/td/tdutils/td/utils/tl_parsers.h @@ -13,11 +13,8 @@ #include "td/utils/Status.h" #include "td/utils/UInt.h" -#include #include #include -#include -#include namespace td { @@ -26,11 +23,7 @@ class TlParser { size_t data_len = 0; size_t left_len = 0; size_t error_pos = std::numeric_limits::max(); - std::string error; - - std::unique_ptr data_buf; - static constexpr size_t SMALL_DATA_ARRAY_SIZE = 6; - std::array small_data_array; + string error; alignas(4) static const unsigned char empty_data[sizeof(UInt512)]; diff --git a/versions.json b/versions.json index 7816fd08cd..6b7e5549eb 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "11.14.1", + "app": "12.0", "xcode": "16.2", "bazel": "8.3.1:0cac3a67dc5429c68272dc6944104952e9e4cf84b29d126a5ff3fbaa59045217", "macos": "15"